2011-01-05 13 views
9

Chcieliśmy ulepszyć nasze projekty z ASP.NET MVC 2 do 3. Większość naszych testów się powiodła, ale są pewne, które zawiodły na ValueProviderFactories.Factories.GetValueProvider(context).Jak testować jednostki ValueProviderFactories w ASP.NET MVC3?

Oto prosta klasa testowa, która ilustruje problem.

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

Wyjątek "Obiekt nie jest ustawiony na wystąpienie obiektu." jest wywoływany, gdy wywoływana jest nazwa ValueProviderFactories.Factories.GetValueProvider(context). StackTrace wygląda następująco:

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# 

Chciałem wiedzieć, dlaczego to rzuca wyjątek i zobaczyłem:

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

Tak, jesteśmy z powrotem w przeszłości, kiedy były zależne od HttpContext.Current? Jak to obejść?

+0

Mam ten sam problem. +1 na dobre pytanie. –

+0

Miałem taką samą potrzebę, więc dziękuję. Jedna rzecz, o której warto wspomnieć, samo ustawienie nowej RouteData() może nadal powodować błędy. Aby go pokonać, musiałem dodać klucz/wartość "kontroler" i "akcja" do routedata przed jego podaniem. – uadrive

Odpowiedz

8

To może być łatwo rozwiązane przez dostawców Proxy, którzy uzyskują dostęp do HttpContext do ignorującego go.

Wyjaśniłem wszystko w moim blogu: Unit test actions with ValueProviderFactories in ASP.NET MVC3.

Kluczem jest ten kod:

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

więc może być używany jako:

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

To było rzeczywiście bardzo proste :)

UPDATE: Jak wspomniano w komentarzach powinieneś pamiętać:

  1. Ustaw właściwość dla pewnej wartości nie-pustej, w przeciwnym razie JsonValueProviderFactory rzuci wyjątek. Wolę tworzyć fałszywe i ustawić domyślną wartość tam.
  2. zastąpienie HttpFileCollectionValueProviderFactory, ponieważ może być używane podczas bindowania.
  3. Uważaj na inne zależności, które możesz mieć w projekcie.
+1

Dobra robota! Uwaga: musisz ustawić właściwość ctx.HttpContext.Request.ContentType na pewną niepustą wartość, w przeciwnym razie JsonValueProviderFactory rzuci wyjątek. – dkl

+0

Problem rozwiązany, dzięki :) – stej

+1

Musiałem również zastąpić 'HttpFileCollectionValueProviderFactory', aby to działało. –

0

Nie powinieneś wywoływać funkcji ValueProviderFactories.Factories, ModelBinders.Binders ani żadnego innego statycznego narzędzia dostępowego z poziomu testu urządzenia. Właśnie dlatego istnieje ModelBindingContext.ValueProvider - tak, abyś mógł dostarczyć fałszywego dostawcę IValue do swojego własnego stworzenia zamiast polegać na statycznych wartościach domyślnych (które zakładają działanie potoku MVC).

+4

Kpiące '' IValueProvider' to śmieszna ilość pracy. Będzie wymagać ponownego wdrożenia kilkudziesięciu funkcji ASP.NET MVC. Nie jest nawet zabawne, jak kiedyś można było je przetestować, a teraz NIE jest. –

Powiązane problemy