Asp.net

如何在 ASP.NET MVC3 中對 ValueProviderFactories 進行單元測試?

  • January 25, 2012

我們想將我們的項目從 ASP.NET MVC 2 升級到 3。我們的大多數測試都成功了,但也有一些在ValueProviderFactories.Factories.GetValueProvider(context).

這是一個說明問題的簡單測試類。

[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?如何解決它?

這可以通過代理訪問 HttpContext 到忽略它的 ValueProvider 來輕鬆解決。

我已經在我的部落格文章中解釋了所有內容:在 ASP.NET MVC3 中使用 ValueProviderFactories 進行單元測試操作

關鍵是這段程式碼:

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);

這實際上很容易:)

更新:如評論中所述,您應該記住:

  1. ctx.HttpContext.Request.ContentType屬性設置為某個非空值,否則 JsonValueProviderFactory 將拋出異常。我更喜歡在那裡創建一個模擬並設置預設值。
  2. 替換它,HttpFileCollectionValueProviderFactory因為它可以在綁定期間使用。
  3. 注意項目中可能存在的其他依賴項。

引用自:https://stackoverflow.com/questions/4603741