2011-01-05 50 views
9

我們希望將我們的項目從ASP.NET MVC 2升級到3.我們的大多數測試都成功了,但有一些失敗,在ValueProviderFactories.Factories.GetValueProvider(context)如何在ASP.NET MVC3中測試ValueProviderFactories?

下面是一個簡單的測試類,用於說明問題。

[TestFixture] 
public class FailingTest 
{ 
    [Test] 
    public void Test() 
    { 
    var type = typeof(string); 
    // any controller 
    AuthenticationController c = new AuthenticationController(); 
    var httpContext = new Mock<HttpContextBase>(); 
    var context = c.ControllerContext = new ControllerContext(httpContext.Object, new RouteData(), c); 

    IModelBinder converter = ModelBinders.Binders.GetBinder(type); 
    var bc = new ModelBindingContext 
    { 
     ModelName = "testparam", 
     ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, type), 
     ValueProvider = ValueProviderFactories.Factories.GetValueProvider(context) 
    }; 
    Console.WriteLine(converter.BindModel(context, bc)); 
    } 
} 

異常「對象引用未設置爲對象的實例」。在調用ValueProviderFactories.Factories.GetValueProvider(context)時拋出。堆棧跟蹤看起來是這樣的:

Microsoft.Web.Infrastructure.dll!Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.CollectionReplacer.GetUnvalidatedCollections(System.Web.HttpContext context) + 0x23 bytes 
Microsoft.Web.Infrastructure.dll!Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.GetUnvalidatedCollections(System.Web.HttpContext context, out System.Collections.Specialized.NameValueCollection form, out System.Collections.Specialized.NameValueCollection queryString, out System.Collections.Specialized.NameValueCollection headers, out System.Web.HttpCookieCollection cookies) + 0xbe bytes  
System.Web.WebPages.dll!System.Web.Helpers.Validation.Unvalidated(System.Web.HttpRequest request) + 0x73 bytes 
System.Web.WebPages.dll!System.Web.Helpers.Validation.Unvalidated(System.Web.HttpRequestBase request) + 0x25 bytes 
System.Web.Mvc.DLL!System.Web.Mvc.FormValueProviderFactory..ctor.AnonymousMethod__0(System.Web.Mvc.ControllerContext cc) + 0x5a bytes 
System.Web.Mvc.DLL!System.Web.Mvc.FormValueProviderFactory.GetValueProvider(System.Web.Mvc.ControllerContext controllerContext) + 0xa0 bytes  
System.Web.Mvc.DLL!System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider.AnonymousMethod__7(System.Web.Mvc.ValueProviderFactory factory) + 0x4a bytes 
System.Core.dll!System.Linq.Enumerable.WhereSelectEnumerableIterator<System.Web.Mvc.ValueProviderFactory,<>f__AnonymousType2<System.Web.Mvc.ValueProviderFactory,System.Web.Mvc.IValueProvider>>.MoveNext() + 0x24d bytes 
System.Core.dll!System.Linq.Enumerable.WhereSelectEnumerableIterator<<>f__AnonymousType2<System.Web.Mvc.ValueProviderFactory,System.Web.Mvc.IValueProvider>,System.Web.Mvc.IValueProvider>.MoveNext() + 0x2ba bytes 
mscorlib.dll!System.Collections.Generic.List<System.Web.Mvc.IValueProvider>.List(System.Collections.Generic.IEnumerable<System.Web.Mvc.IValueProvider> collection) + 0x1d8 bytes  
System.Core.dll!System.Linq.Enumerable.ToList<System.Web.Mvc.IValueProvider>(System.Collections.Generic.IEnumerable<System.Web.Mvc.IValueProvider> source) + 0xb5 bytes 
System.Web.Mvc.DLL!System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(System.Web.Mvc.ControllerContext controllerContext) + 0x24d bytes 
test.DLL!FailingTest.Test() Line 31 + 0xf9 bytes C# 

我想知道爲什麼它會拋出異常,只見原因:

public static ValidationUtility.UnvalidatedCollections GetUnvalidatedCollections(HttpContext context) 
{ 
    return (ValidationUtility.UnvalidatedCollections) context.Items[_unvalidatedCollectionsKey]; 
} 

那麼,我們回到過去,當我們依賴於HttpContext.Current?如何解決它?

+0

我有同樣的問題。 +1是一個很好的問題。 – 2011-03-17 05:34:10

+0

我有同樣的需要,所以謝謝。有一點需要提及,只需設置新的RouteData()仍然可以引發錯誤。爲了解決這個問題,我必須在餵食之前在路由數據中添加一個「控制器」和「動作」鍵/值。 – uadrive 2011-10-25 21:00:42

回答

8

這可以很容易地通過代理訪問HttpContext的ValueProviders來解決,忽略它。

我已經在我的博客文章中解釋了所有內容:Unit test actions with ValueProviderFactories in ASP.NET MVC3

的關鍵是這段代碼:

public static class ValueProviderFactoresExtensions { 
    public static ValueProviderFactoryCollection ReplaceWith<TOriginal>(this ValueProviderFactoryCollection factories, Func<ControllerContext, NameValueCollection> sourceAccessor) { 
     var original = factories.FirstOrDefault(x => typeof(TOriginal) == x.GetType()); 
     if (original != null) { 
      var index = factories.IndexOf(original); 
      factories[index] = new TestValueProviderFactory(sourceAccessor); 
     } 
     return factories; 
    } 

    class TestValueProviderFactory : ValueProviderFactory { 
     private readonly Func<ControllerContext, NameValueCollection> sourceAccessor; 


     public TestValueProviderFactory(Func<ControllerContext, NameValueCollection> sourceAccessor) { 
      this.sourceAccessor = sourceAccessor; 
     } 


     public override IValueProvider GetValueProvider(ControllerContext controllerContext) { 
      return new NameValueCollectionValueProvider(sourceAccessor(controllerContext), CultureInfo.CurrentCulture); 
     } 
    }   
} 

因此,它可以作爲:

ValueProviderFactories.Factories 
    .ReplaceWith<FormValueProviderFactory>(ctx => ctx.HttpContext.Request.Form) 
    .ReplaceWith<QueryStringValueProviderFactory>(ctx => ctx.HttpContext.Request.QueryString); 

它實際上是很容易的:)

UPDATE:正如在評論中提到你應該記住:

  1. 設置屬性爲一些非空值,否則JsonValueProviderFactory會拋出異常。我更喜歡在那裏創建一個模擬並設置默認值。
  2. 取代HttpFileCollectionValueProviderFactory,因爲它可以在綁定過程中使用。
  3. 注意您可能在項目中存在的其他依賴關係。
+1

幹得好!注意:您必須將ctx.HttpContext.Request.ContentType屬性設置爲某個非空值,否則JsonValueProviderFactory將引發異常。 – dkl 2011-03-18 11:57:01

+0

問題解決了,謝謝:) – stej 2011-03-18 12:17:14

+1

我也必須替換'HttpFileCollectionValueProviderFactory'才能使它工作。 – 2011-03-29 11:02:45

0

您不應該在單元測試中調用ValueProviderFactories.Factories,ModelBinders.Binders或任何其他靜態訪問器。這就是爲什麼ModelBindingContext.ValueProvider存在的原因 - 這樣您就可以提供自己創建的模擬IValueProvider,而不是依賴靜態默認值(假定MVC管道正在運行)。

+4

嘲笑'IValueProvider'是一個荒謬的工作量。它需要重新實現幾十個ASP.NET MVC功能。它曾經是可測試的,但它現在不是。 – 2011-03-17 05:45:37