2010-03-09 10 views
24

He estado repasando el proceso de limpieza de nuestro código de controlador para que cada acción sea comprobable. En términos generales, esto no ha sido demasiado difícil: cuando tenemos la oportunidad de utilizar un objeto fijo, como por ejemplo FormsAuthentication, generalmente presentamos algún tipo de contenedor según corresponda y estamos a nuestro gusto.¿Es HttpContextWrapper todo eso ... útil?

Por razones no particularmente germaine a esta conversación, cuando se trata de tratar con el uso de HttpContext, decidimos usar la clase HttpContextWrapper recién creada en lugar de inventar algo de cosecha propia. Algo que sí introdujimos fue la capacidad de intercambiar HttpContextWrapper (por ejemplo, para pruebas unitarias). Esto fue totalmente inspirada en la forma de Oren Eini maneja la unidad de pruebas con DateTime (see article, un patrón también utilizamos)

public static class FooHttpContext 
{ 
    public static Func<HttpContextWrapper> Current =() 
     => new HttpContextWrapper(HttpContext.Current); 

    public static void Reset() 
    { 
     Current =() => new HttpContextWrapper(HttpContext.Current); 
    } 
} 

Nada particularmente elegante. Y funciona bien en nuestro código de controlador. El golpeador vino cuando vamos a escribir pruebas unitarias. Estamos utilizando Moq como nuestro marco de burla, pero por desgracia

var context = new Mock<HttpContextWrapper>() 

rompe desde HttpContextWrapper no tiene un ctor sin parámetros. ¿Y qué se necesita como parámetro ctor? Un objeto HttpContext. Así que me encuentro en una trampa 22.

Estoy usando la forma prescrita para desacoplar HttpContext, pero no puedo simular un valor porque el objeto original HttpContext estaba sellado y, por lo tanto, era difícil de probar. Puedo mapear HttpContextBase, del cual ambos derivan, pero eso realmente no me da lo que busco. ¿Me estoy perdiendo el punto en algún lugar con respecto a HttpContextWrapper?

Editar para aclarar la intención

Hemos encontrado maneras de resolver el problema - pero supongo que la última pregunta que estamos se va con qué valor es HttpContextWrapper pone sobre la mesa? ¡No dudo de que alguien haya tenido un a-ha por completo! momento con eso, pero simplemente no viene a mí. La mayoría de las publicaciones que veo aquí lo analizo en términos de capacidad de prueba, pero mi propia experiencia me ha llevado a creer que no aportó mucho en ese contexto. A menos que lo estemos haciendo mal. (Totalmente posible).

Respuesta

30

Debe utilizar el resumen HttpContextBase que es mucho más fácil de simular en lugar de HttpContextWrapper.

public static Func<HttpContextBase> Current = 
    () => new HttpContextWrapper(HttpContext.Current); 

Y en la prueba de unidad:

SomeClass.Current = MockHttpContextBase(); // Sorry I don't know the syntax for Moq 
+0

Usted' Estoy absolutamente en lo cierto, y lo que finalmente hicimos ... pero mi pregunta fue más sobre lo que Wrapper realmente trae a la mesa, déjenme editar para mayor claridad. – bakasan

+1

'HttpContextWrapper' no pretende ser una abstracción sino una implementación concreta de' HttpContextBase'. Creo que es valioso porque oculta algunos métodos estáticos e internos de 'HttpContext'. –

+7

'HttpContextWrapper' implementa el (mockable)' HttpContextBase' mediante el reenvío de llamadas al ASP.NET-vintage 'HttpContext'. Es una vergüenza eludir el hecho de que 'HttpContext' no se puede burlar. –

31

Este blog lo explica muy bien:

http://splinter.com.au/httpcontext-vs-httpcontextbase-vs-httpcontext

El punto es que 'vintage' HttpContext no implementa HttpContextBase, y no es virtual, y por lo tanto no se puede burlar. HttpContextBase se introdujo en 3.5 como una alternativa ficticia. Pero todavía existe el problema de que HttpContext vintage no implementa HttpContextBase.

Así HttpContextWrapper es una clase contenedora mano (o 'chapuza') que no implementar HttpContextBase, y se puede utilizar cuando la inyección de un 'real' HttpContext usando COI, por lo general con un método de fábrica de esta manera: () => new HttpContextWrapper(HttpContext.Current)

+4

Y configuraría el registro en Unity de la siguiente manera; 'var container = new UnityContainer(); container.RegisterType (new InjectionConstructor (HttpContext.Current)); ' –