2010-03-13 7 views
20

Tengo un controlador que implementa una simple operación de Agregar de una entidad y redirige a la página de detalles:Unidad de prueba de un controlador en ASP.NET MVC 2 con RedirectToAction

[HttpPost] 
public ActionResult Add(Thing thing) 
{ 
    // ... do validation, db stuff ... 
    return this.RedirectToAction<c => c.Details(thing.Id)); 
} 

Esto funciona muy bien (con el RedirectToAction de el ensamblaje MvcContrib).

Cuando estoy probando unitariamente este método, quiero acceder al ViewData que se devuelve desde la acción Detalles (para poder obtener la clave primaria de la cosa recién insertada y probar que ahora está en la base de datos).

La prueba tiene:

var result = controller.Add(thing); 

Pero resultado aquí es de tipo: System.Web.Mvc.RedirectToRouteResult (que es un System.Web.Mvc.ActionResult). Todavía no ha ejecutado el método Detalles.

He intentado llamar ExecuteResult en el objeto devuelto que pasa en un ControllerContext imitado pero el marco no estaba contento con la falta de detalles en el objeto burlado.

Podría intentar completar los detalles, etc., pero luego mi código de prueba es mucho más largo que el código que estoy probando y creo que necesito pruebas unitarias para las pruebas de la unidad.

¿Me falta algo en la filosofía de las pruebas? ¿Cómo pruebo esta acción cuando no puedo obtener su estado devuelto?

Respuesta

7

Parece que está haciendo demasiado para una prueba de unidad. La validación y el acceso a los datos normalmente los realizarían los servicios a los que llamas desde la acción del controlador. Se burla de esos servicios y solo prueba que fueron llamados correctamente.

Algo como esto (utilizando la sintaxis aproximado para Rhino.Mocks & NUnit):

[Test] 
public void Add_SavesThingToDB() 
{ 
    var dbMock = MockRepository.GenerateMock<DBService>(); 
    dbMock.Expect(x => x.Save(thing)).Repeat.Once(); 

    var controller = new MyController(dbMock); 
    controller.Add(new Thing()); 

    dbMock.VerifyAllExpectations(); 
} 

[Test] 
public void Add_RedirectsAfterSave() 
{ 
    var dbMock = MockRepository.GenerateMock<DBService>(); 

    var controller = new MyController(dbMock); 
    var result = (RedirectToRouteResult)controller.Add(new Thing()); 

    Assert.That(result.Url, Is.EqualTo("/mynew/url")); 
} 
+0

Gracias - eso definitivamente ayuda a evitar las dificultades con el marco durante la prueba. Entonces, siguiendo este modismo, tendría pruebas unitarias para el servicio DBServicio que demuestra que puedo agregar cosas, y una prueba unitaria para el controlador que demuestre que sus llamadas se guardan en el servicio. Pero realmente no he probado que lo que pasó al controlador termine en la base de datos. Tal vez podría hacer eso con un montón de reglas de burla más complejas ... pero eso no se siente bien, es una gran prueba de la placa de la caldera para una operación simple. –

+0

Bueno, determinar el alcance y el esfuerzo para poner en pruebas y qué probar, etc., es algo con lo que también me esfuerzo. Creo que deberías tratar de separar tus pruebas en dos categorías: pruebas unitarias y pruebas de integración. Las pruebas unitarias deben probar solo unidades de funcionalidad muy pequeñas, como las pruebas anteriores. Las pruebas de integración deberían ver cómo se integra todo, tal vez cubriendo una pequeña historia de usuario que usted tiene. Preferiría hacer las pruebas de integración lo más cerca posible del uso "real", por ejemplo ejecutando WatiN y haciendo clic en un par de enlaces. No hay necesidad de burlarse allí en absoluto. – rmac

+0

Si prueba su DBService, prueba que funciona. Entonces, debe suponer que puede y manejará las llamadas a la base de datos correctamente. Entonces, si su controlador usa este servicio, usted sabe que funcionará. Con el marco simulado, puede validar los parámetros que se pasan a los métodos de servicio y esto es suficiente. Creo que rmacfie tiene razón, quizás estés intentando probar mucho más profundo. La prueba de unidad debe cubrir solo una acción, no todo el proceso. –

12

Hay MVC Contrib TestHelper que son fantásticos para probar la mayor parte del ActionResult

Puede obtenerlo aquí: http://mvccontrib.codeplex.com/wikipage?title=TestHelper

Aquí es un ejemplo de la sintaxis:

var controller = new TestController(); 

controller.Add(thing) 
      .AssertActionRedirect() 
      .ToAction<TestController>(x => x.Index()); 

Para comprobar si los datos se han conservado correctamente, debería preguntar directamente a su base de datos, no sé si está utilizando un ORM o algo, pero debe hacer algo para obtener el último elemento insertado en su base de datos, luego compare con el valor que proporcionó a su Add ActionResult y vea si esto está bien.

No creo que probar su Details ActionResult para ver si sus datos se mantienen es el enfoque correcto. Eso no sería una prueba unitaria, más una prueba funcional.

Pero también debe probar su unidad Detalles método para asegurarse de que su objeto viewdata se llena con los datos correctos procedentes de su base de datos.

31

Estoy usando MVC2 RC2 en este momento y la respuesta de rmacfie no me funcionó, pero me puso en el camino correcto.

razón o sin ella me las arreglé para hacer esto en mi prueba en su lugar:

var actionResult = (RedirectToRouteResult)logonController.ForgotUsername(model); 

actionResult.RouteValues["action"].should_be_equal_to("Index"); 
actionResult.RouteValues["controller"].should_be_equal_to("Logon"); 

No estoy seguro si esto va a ayudar a alguien, pero podría ahorrar 10 minutos.

+2

¡Impresionante! Puede verificar valores de ruta adicionales de manera similar. Para 'RedirectToAction (" Detalles "," Persona ", {personId = 123})' puede marcar 'personId':' Assert.AreEqual (123, actionResult.RouteValues ​​["personId"]) ' – Kirill

6

Tengo un método de ayuda estático que prueba la redirección.

public static class UnitTestHelpers 
{ 
    public static void ShouldEqual<T>(this T actualValue, T expectedValue) 
    { 
     Assert.AreEqual(expectedValue, actualValue); 
    } 

    public static void ShouldBeRedirectionTo(this ActionResult actionResult, object expectedRouteValues) 
    { 
     RouteValueDictionary actualValues = ((RedirectToRouteResult)actionResult).RouteValues; 
     var expectedValues = new RouteValueDictionary(expectedRouteValues); 

     foreach (string key in expectedValues.Keys) 
     { 
      Assert.AreEqual(expectedValues[key], actualValues[key]); 
     } 
    } 
} 

Luego, crear una prueba de redirección es muy fácil.

[Test] 
public void ResirectionTest() 
{ 
    var result = controller.Action(); 

    result.ShouldBeRedirectionTo(
     new 
     { 
      controller = "ControllerName", 
      action = "Index" 
     } 
    ); 
} 
+3

+1 para el método de extensión pero por alguna razón, el nombre del controlador MVC3 es nulo –

Cuestiones relacionadas