ASP.NET MVC: controladores de pruebas unitarias que usan UrlHelper

170

Una de mis acciones de controladores, una que se llama en una solicitud de Ajax, es devolver una URL al lado del cliente para que pueda hacer una redirección. Estoy usando Url.RouteUrl(..)y durante mis pruebas unitarias esto falla ya que el Controller.Urlparámetro no está precargado.

He intentado muchas cosas, entre otras que tratan de talón UrlHelper(que no), crear manualmente una UrlHelpercon una RequestContextque tiene un apagó HttpContextBase(que fracasó en una RouteCollection.GetUrlWithApplicationPathllamada).

Busqué en Google pero no encontré prácticamente nada sobre el tema. ¿Estoy haciendo algo increíblemente estúpido Url.RouteUrlen mi acción Controlador? hay una manera mas facil?

Para empeorar las cosas, me gustaría poder probar la URL devuelta en mi prueba unitaria; de hecho, solo me interesa saber si está redirigiendo a la ruta correcta, pero dado que estoy devolviendo una URL en lugar de una ruta, me gustaría controlar la URL que se resuelve (por ejemplo, mediante el uso de un trozo RouteCollection), pero para empezar, estaré encantado de pasar mi prueba.

efdee
fuente

Respuestas:

202

Aquí está una de mis pruebas (xUnit + Moq) solo para casos similares (usando Url.RouteUrl en el controlador)

Espero que esto ayude:

var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);

var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());

var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
response.Setup(x => x.ApplyAppPathModifier("/post1")).Returns("http://localhost/post1");

var context = new Mock<HttpContextBase>(MockBehavior.Strict);
context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);

var controller = new LinkbackController(dbF.Object);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
eu-ge-ne
fuente
2
Por el momento, elegí una solución en la que abstraía las llamadas a UrlHelper para poder interceptarlas. Sin embargo, gracias por su fragmento, me ahorrará mucho tiempo descubrir cómo burlarse correctamente de un Contexto de Solicitud / Respuesta / Controlador.
efdee
Gracias por la respuesta @ eu-ge-ne, también me ayudó mucho. He incluido algunas configuraciones más de moq para usar un parámetro de colección de formularios utilizado por UpdateModel
jebcrum
16
+1 excelente. Aunque un consejo: lo uso como MockHelper y cambio la respuesta. Configuración de ApplyAppPathModifier a esto: response.Setup (x => x.ApplyAppPathModifier (Moq.It.IsAny <String> ())). Returns ((String url ) => url); Es feo, pero obtengo el objeto serializado de nuevo en forma codificada en URL, en lugar de codificar el valor devuelto.
eduncan911
Eso funciona parcialmente para mí. ¿Alguna idea de por qué obtengo Controller / en lugar de Controller / Action? Mi prueba falla porque no son exactamente iguales y, sin embargo, registro los mismos valores de enrutamiento. Muy extraño ...
Nick
3
La ApplyAppPathModifierparte es la parte crítica para el UrlHelper
Chris S
37

Una implementación modificada de eu-ge-ne. Éste devuelve un enlace generado basado en las rutas definidas en la aplicación. El ejemplo de eu-ge-ne siempre devolvió una respuesta fija. El siguiente enfoque le permitirá probar que la acción / controlador correcto y la información de ruta se transfieren al UrlHelper, que es lo que desea si está probando la llamada al UrlHelper.

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

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

request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new NameValueCollection());

response.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(x => x);

context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);

var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
var helper = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
Steven Pena
fuente
12

Esta publicación puede ser útil si quieres burlarte de la clase HttpContextBase.

http://www.hanselman.com/blog/ASPNETMVCSessionAtMix08TDDAndMvcMockHelpers.aspx

Gerardo Contijoch
fuente
Genial, esto me ayudó, aunque tuve que agregar un código adicional al método FakeHttpContext para detener el estallido de ayuda: context.Setup (ctx => ctx.Request.ApplicationPath) .Returns ("/ AntiBlowup"); También refactoricé el código para que use la sintaxis más reciente de Setup (). Gracias.
RichardOD 01 de
2

A partir de la respuesta de @ eu-ge-ne que me ayudó mucho:

Tuve un ActionResult que hizo una redirección y una llamada UpdateModel con un parámetro FormCollection. Para que el UpdateModel () funcione, tuve que agregar esto a mi HttpRequestBase burlado:

FormCollection collection = new FormCollection();
collection["KeyName"] = "KeyValue";

request.Setup(x => x.Form).Returns(collection);
request.Setup(x => x.QueryString).Returns(new NameValueCollection());

Para probar que la URL redirigida era correcta, puede hacer lo siguiente:

RedirectResult result = controller.ActionName(modelToSubmit, collection) as RedirectResult;
Assert.AreEqual("/Expected/URL", result.Url);
jebcrum
fuente