ASP.NET MVC: Controladores de teste de unidade que usam UrlHelper


170

Uma das ações de meus controladores, que está sendo chamada em uma solicitação do Ajax, está retornando uma URL para o lado do cliente para que ele possa fazer um redirecionamento. Estou usando Url.RouteUrl(..)e durante meus testes de unidade isso falha desde que oController.Url parâmetro não está pré-preenchido.

Tentei várias coisas, entre outras tentando stub UrlHelper(que falharam), criando manualmente um UrlHelpercom a RequestContextque possui uma stub HttpContextBase(que falhou em umRouteCollection.GetUrlWithApplicationPath chamada).

Eu pesquisei no Google, mas não encontrei praticamente nada sobre o assunto. Estou fazendo algo incrivelmente estúpido usandoUrl.RouteUrl na minha ação do Controller? Existe uma maneira mais fácil?

Para piorar, eu gostaria de poder testar o URL retornado no meu teste de unidade - na verdade, só estou interessado em saber que ele está redirecionando para a rota correta, mas como estou retornando um URL em vez de um rota, eu gostaria de controlar o URL que foi resolvido (por exemplo, usando um stubbed RouteCollection) - mas ficarei feliz em concluir a aprovação do teste.

Respostas:


202

Aqui está um dos meus testes (xUnit + Moq) apenas para casos semelhantes (usando Url.RouteUrl no controlador)

Espero que isto ajude:

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

2
Por enquanto, eu escolhi uma solução em que abstraí as chamadas para o UrlHelper para poder interceptá-las. Obrigado pelo seu snippet, no entanto, economizará muito tempo para descobrir como simular corretamente um Request / Response / ControllerContext.
efdee 24/03/09

Obrigado pela resposta @ eu-ge-ne, também me ajudou muito. Eu incluí mais algumas configurações MOQ para usar um parâmetro formcollection usado por UpdateModel
jebcrum

16
+1 excelente. Embora seja uma dica: eu uso isso como um MockHelper e altero a response.Setup for ApplyAppPathModifier para this: response.Setup (x => x.ApplyAppPathModifier (Moq.It.IsAny <String> ())). ) => url); É feio, mas recebo o objeto serializado de volta no formato codificado por URL, em vez de codificar o valor retornado.
eduncan911

Isso parcialmente funciona para mim. Alguma idéia de por que eu recebo Controller / em vez de Controller / Action? Meu teste falha porque eles não são exatamente os mesmos e ainda assim registro os mesmos valores de roteamento. Muito estranho ...
Nick

3
A ApplyAppPathModifierparte é o pouco crítico para o UrlHelper
Chris S

37

Uma implementação modificada da eu-ge-ne. Este retorna um link gerado com base nas rotas definidas no aplicativo. O exemplo da eu-ge-ne sempre retornava uma resposta fixa. A abordagem abaixo permitirá que você teste se as informações corretas sobre ação / controlador e rota estão sendo passadas para o UrlHelper - que é o que você deseja se estiver testando a chamada para o 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);

12

Esta publicação pode ser útil se você quiser zombar da classe HttpContextBase.

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


Legal, isso me ajudou, embora eu tenha que adicionar algum código extra ao método FakeHttpContext para interromper o processo de ajuda: context.Setup (ctx => ctx.Request.ApplicationPath) .Returns ("/ AntiBlowup"); Também refatorei o código para usar a sintaxe Setup () mais recente. Obrigado.
21410 RichardOD

2

Com base na resposta da @ eu-ge-ne, que me ajudou muito:

Eu tinha um ActionResult que fazia um redirecionamento e também tinha uma chamada UpdateModel com um parâmetro FormCollection. Para que o UpdateModel () funcione, tive que adicioná-lo ao meu Mocked HttpRequestBase:

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

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

Para testar se o URL redirecionado estava correto, você pode fazer o seguinte:

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.