example - Unidad de prueba de un controlador en ASP.NET MVC 2 con RedirectToAction
redirecttoaction() (4)
Tengo un controlador que implementa una operación simple Agregar en una entidad y redirige a la página de Detalles:
[HttpPost]
public ActionResult Add(Thing thing)
{
// ... do validation, db stuff ...
return this.RedirectToAction<c => c.Details(thing.Id));
}
Esto funciona muy bien (usando la RedirectToAction del ensamblado MvcContrib).
Cuando estoy haciendo una prueba de unidad de este método, quiero acceder al ViewData que se devuelve desde la acción Detalles (para poder obtener la clave principal 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 el resultado aquí es de tipo: System.Web.Mvc.RedirectToRouteResult
(que es un System.Web.Mvc.ActionResult
). Aún no se ha ejecutado el método de Detalles.
He intentado llamar a ExecuteResult
en el objeto devuelto pasando un ControllerContext
pero el marco no estaba contento con la falta de detalles en el objeto simulado.
Podría intentar completar los detalles, etc., 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 de unidad para las pruebas de unidad.
¿Me estoy perdiendo algo en la filosofía de prueba? ¿Cómo pruebo esta acción cuando no puedo llegar a su estado devuelto?
Estoy usando MVC2 RC2 en este momento y la respuesta de rmacfie no me funcionó del todo, pero sí me guió por el camino correcto.
En forma correcta o incorrecta, logré hacer esto en mi prueba:
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 de si esto ayudará a alguien pero puede ahorrarle 10 minutos.
Hay MVC Contrib TestHelper que son fantásticos para probar la mayoría de los ActionResult
Puede obtenerlo aquí: http://mvccontrib.codeplex.com/wikipage?title=TestHelper
Aquí hay 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, tal vez debería preguntar directamente a su base de datos. No sé si está usando un ORM o algo así, pero debería 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 la prueba de sus Detalles ActionResult para ver si sus datos se mantienen es el enfoque correcto. Eso no sería una prueba de unidad, más una prueba funcional.
Pero también debe realizar una prueba unitaria de su método de Detalles para asegurarse de que su objeto viewdata esté lleno con los datos correctos que provienen de su base de datos.
Parece que estás haciendo demasiado para una prueba de unidad. La validación y el acceso a los datos se realizarían normalmente mediante servicios a los que llama desde la acción del controlador. Te burlas de esos servicios y solo pruebas que fueron llamados apropiadamente.
Algo como esto (usando la sintaxis aproximada 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"));
}
Tengo un método auxiliar 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]);
}
}
}
Entonces 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"
}
);
}