2009-08-04 21 views
21

Tengo un accesorio de prueba de unidad en el que estoy tratando de probar un ControllerAction en un controlador ASP.NET MVC que se utiliza para las funciones de membresía en una aplicación web. Estoy intentando burlarme del HttpContext para las pruebas. ControllerAction en prueba realmente establece propiedades en HttpContext, como valores de sesión, valores de Response.Cookies, etc. Esto no es todo el código, pero aquí hay una muestra aproximada de la prueba que estoy tratando de ejecutar :Burlarse de HttpContextBase con Moq

[Test] 
public void ValidRegistrationDataSuccessfullyCreatesAndRegistersUser() 
{ 
    var context = new Mock<HttpContextBase>() {DefaultValue = DefaultValue.Mock}; 
    context.SetupAllProperties(); 
    var provider = new Mock<MembershipProvider>(new object[] {context.Object}); 
    var controller = new AccountController(context.Object, provider.Object); 
    // This just sets up a local FormCollection object with valid user data 
    // in it to use to attempt the registration 
    InitializeValidFormData(); 
    ActionResult result = controller.Register(_registrationData); 
    Assert.IsInstanceOfType(typeof(ViewResult), result); 
    // Here is where I'd like to attempt to do Assertions against properties 
    // of the HttpContext, like ensuring that a Session object called "User" 
    // exists, and new auth cookie exists on the Response.Cookies collection. 
    // So far I've been unable to successfully check the values of those properties. 
    // I've been unsuccessful in getting those properties setup correctly on my 
    // mock object so that my ControllerAction can actually *set* their values, 
    // and that I can make assertions on them afterwards. The above code actually 
    // generates a StackOverflowException (which I've reported) on the 
    // context.SetupAllProperties() call. What am I doing wrong, or what do I need 
    // to do to be able to set and assert on those context properties? 
} 

No está seguro de lo que estoy haciendo mal, pero me encantaría que si alguien me podría apuntar en la dirección correcta y me muestran cómo configurar este objeto HttpContextBase simulacro de tal manera que mi controlador puede realmente establecer valores en sus propiedades, y puedo hacer afirmaciones sobre esas propiedades para asegurar que mi ControllerAction está haciendo lo que necesito.

¿Me estoy acercando a esto de la manera incorrecta? Sé que los controladores MVC tienen un ControllerContext que puedo usar para establecer valores para Session, etc., pero no puedo imaginar cómo se podría burlar de algo así sin inyectarlo. ¿Hay alguna forma de hacerlo en su lugar? (También necesito poder pasar el contexto a mi proveedor de membresía) ¿Sería ese un mejor enfoque?

Gracias.

Respuesta

31

Estoy usando una versión de algún código que Steve Sanderson incluyó en su Pro Asp.NET MVC book ... y actualmente estoy teniendo un dilema moral si está bien publicar el código aquí. ¿Qué tal si me comprometo con una versión muy despojada? ;)

De modo que esto se puede reutilizar fácilmente, cree una clase similar a la siguiente a la que pasará su controlador. Esto configurará sus simulacros y los puso a ControllerContext

de su controlador
public class ContextMocks 
{ 
    public Moq.Mock<HttpContextBase> HttpContext { get; set; } 
    public Moq.Mock<HttpRequestBase> Request { get; set; } 
    public RouteData RouteData { get; set; } 

    public ContextMocks(Controller controller) 
    { 
     //define context objects 
     HttpContext = new Moq.Mock<HttpContextBase>(); 
     HttpContext.Setup(x => x.Request).Returns(Request.Object); 
     //you would setup Response, Session, etc similarly with either mocks or fakes 

     //apply context to controller 
     RequestContext rc = new RequestContext(HttpContext.Object, new RouteData()); 
     controller.ControllerContext = new ControllerContext(rc, controller); 
    } 
} 

Y luego, en el método de ensayo que acababa de crear una instancia de ContextMocks y pase el objeto controlador que está probando:

[Test] 
Public void test() 
{ 
    var mocks = new ContextMocks(controller); 
    var req = controller.Request; 
    //do some asserts on Request object 
} 

Parece muy similar a los ejemplos de Craig, pero esto es con Moq v3. Tengo que darle apoyo a Steve Sanderson para esto; lo estoy usando como base para probar todo tipo de cosas que de otra manera tradicionalmente serían difíciles de probar: cookies, sesión, método de solicitud, querystring ¡y más!

+0

Tengo su libro, pero solo lo tengo hace un par de días, así que supongo que todavía no he llegado a esa parte. Si pudieras dirigirme a la sección de ese libro de donde obtuviste el código de muestra, sería fantástico. Muchas gracias. –

+0

Mira el final de ch.9. No lo tengo enfrente, pero parece ser que está mirando la tabla de contenido del libro de Amazon. –

+0

Lo encontré. Eso fue * exactamente * lo que necesitaba. Lo tengo todo funcionando ahora. Muchas gracias. –

22

Here's how I do it.

public static HttpContextBase FakeHttpContext() 
    { 
     var context = new Mock<HttpContextBase>(); 
     var request = new Mock<HttpRequestBase>(); 
     var response = new Mock<HttpResponseBase>(); 
     var session = new Mock<HttpSessionStateBase>(); 
     var server = new Mock<HttpServerUtilityBase>(); 
     var user = new Mock<IPrincipal>(); 
     var identity = new Mock<IIdentity>(); 

     request.Expect(req => req.ApplicationPath).Returns("~/"); 
     request.Expect(req => req.AppRelativeCurrentExecutionFilePath).Returns("~/"); 
     request.Expect(req => req.PathInfo).Returns(string.Empty); 
     response.Expect(res => res.ApplyAppPathModifier(It.IsAny<string>())) 
      .Returns((string virtualPath) => virtualPath); 
     user.Expect(usr => usr.Identity).Returns(identity.Object); 
     identity.ExpectGet(ident => ident.IsAuthenticated).Returns(true); 

     context.Expect(ctx => ctx.Request).Returns(request.Object); 
     context.Expect(ctx => ctx.Response).Returns(response.Object); 
     context.Expect(ctx => ctx.Session).Returns(session.Object); 
     context.Expect(ctx => ctx.Server).Returns(server.Object); 
     context.Expect(ctx => ctx.User).Returns(user.Object); 

     return context.Object; 
    } 

Esta es una versión mejorada de the MvcMockHelpers library released by Scott Hanselman. Este es el código Moq 2.0; la sintaxis es ligeramente diferente en 3.

+0

Sí, gracias, pero estoy usando Moq 3.1.416.3, por lo que sería útil ver la sintaxis para esa versión. Principalmente porque, aparentemente, las propiedades de "configuración" son bastante diferentes en 3.x si entiendo correctamente. Gracias por el ejemplo sin embargo. –

+6

Reemplazar Espere con la instalación y debería funcionar tal como está. –

+0

'request.Expect (req => req.ApplicationPath) .Returns (" ~/");' debe ser 'request.Expect (req => req.ApplicationPath) .Returns ("/");' –

Cuestiones relacionadas