2011-01-05 17 views
9

Queríamos actualizar nuestros proyectos de ASP.NET MVC 2 a 3. La mayoría de nuestras pruebas tuvieron éxito, pero hay algunas que fallan en ValueProviderFactories.Factories.GetValueProvider(context).Cómo probar ValueProviderFactories en ASP.NET MVC3?

Aquí hay una clase de prueba simple que ilustra el problema.

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

Excepción "Referencia de objeto no establecida en una instancia de un objeto." se lanza cuando se llama ValueProviderFactories.Factories.GetValueProvider(context). El StackTrace se ve así:

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# 

lo que quería saber la razón por la que arroja la excepción y sierra:

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

Entonces, ¿estamos de vuelta en el pasado, cuando dependíamos de HttpContext.Current? ¿Cómo solucionarlo?

+0

Tengo el mismo problema. +1 para una buena pregunta. –

+0

Tenía la misma necesidad, así que gracias. Un punto para mencionar, simplemente establecer el nuevo RouteData() aún puede arrojar errores. Para superarlo, tuve que agregar una clave/valor de "controlador" y "acción" a los datos de ruta antes de alimentarlo. – uadrive

Respuesta

8

Esto se puede resolver fácilmente recurriendo a ValueProviders que acceden a HttpContext a uno que lo ignora.

He explicado todo en mi publicación de blog: Unit test actions with ValueProviderFactories in ASP.NET MVC3.

La clave es el código:

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

Por lo tanto, se puede utilizar como:

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

Fue realmente muy fácil :)

ACTUALIZACIÓN: Como se mencionó en los comentarios debe recordar:

  1. establece propiedad en algún valor no nulo; de lo contrario, JsonValueProviderFactory emitirá una excepción. Prefiero crear un simulacro y establecer el valor predeterminado allí.
  2. reemplace el HttpFileCollectionValueProviderFactory, ya que se puede utilizar durante el enlace.
  3. ten cuidado con otras dependencias que puedas tener en el proyecto.
+1

¡Buen trabajo! Nota: debe establecer la propiedad ctx.HttpContext.Request.ContentType en algún valor no nulo; de lo contrario, JsonValueProviderFactory emitirá una excepción. – dkl

+0

Problema resuelto, gracias :) – stej

+1

También tuve que reemplazar 'HttpFileCollectionValueProviderFactory' para que esto funcione. –

0

No debe llamar a ValueProviderFactories.Factories, ModelBinders.Binders ni a ningún otro accesorio estático desde una prueba unitaria. Es precisamente por eso que existe ModelBindingContext.ValueProvider, para que pueda proporcionar un IValueProvider simulado de su propia creación en lugar de confiar en los valores predeterminados estáticos (que suponen que se está ejecutando la interconexión de MVC).

+4

Burlarse 'IValueProvider' es una cantidad ridícula de trabajo. Requerirá la reimplementación de docenas de las características de ASP.NET MVC. Ni siquiera es gracioso cómo solía ser comprobable, y ahora NO lo es. –