ASP.NET MVC: contrôleurs de test unitaire qui utilisent UrlHelper

170

L'une de mes actions de contrôleurs, qui est appelée dans une requête Ajax, renvoie une URL au côté client afin qu'il puisse effectuer une redirection. J'utilise Url.RouteUrl(..)et pendant mes tests unitaires, cela échoue car le Controller.Urlparamètre n'est pas pré-rempli.

J'ai essayé beaucoup de choses, entre autres en essayant de stub UrlHelper(qui a échoué), en créant manuellement un UrlHelperavec un RequestContextqui a un stubbed HttpContextBase(qui a échoué sur un RouteCollection.GetUrlWithApplicationPathappel).

J'ai cherché sur Google mais je n'ai pratiquement rien trouvé sur le sujet. Est-ce que je fais quelque chose d'incroyablement stupide en utilisant Url.RouteUrlmon action de contrôleur? Y a-t-il un moyen plus simple?

Pour aggraver les choses, j'aimerais pouvoir tester l'URL renvoyée dans mon test unitaire - en fait, je suis seulement intéressé à savoir qu'il redirige vers la bonne route, mais puisque je retourne une URL au lieu d'un route, je voudrais contrôler l'URL qui est résolue (par exemple en utilisant un stubbed RouteCollection) - mais je serai heureux de faire passer mon test pour commencer.

efdee
la source

Réponses:

202

Voici l'un de mes tests (xUnit + Moq) juste pour un cas similaire (en utilisant Url.RouteUrl dans le contrôleur)

J'espère que cela t'aides:

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);
Eugène
la source
2
Pour le moment, je suis allé avec une solution où j'ai abstrait les appels à UrlHelper afin que je puisse les intercepter. Merci pour votre extrait de code cependant, cela me fera gagner beaucoup de temps pour comprendre comment se moquer correctement d'une demande / réponse / ControllerContext.
efdee
Merci pour la réponse @ eu-ge-ne, cela m'a beaucoup aidé aussi. J'ai inclus d'autres configurations de moq pour utiliser un paramètre formcollection utilisé par UpdateModel
jebcrum
16
+1 excellent. Bien qu'un conseil: j'utilise ceci comme un MockHelper et change la response.Setup for ApplyAppPathModifier en ceci: response.Setup (x => x.ApplyAppPathModifier (Moq.It.IsAny <String> ())). Renvoie ((String url ) => url); C'est moche, mais je récupère l'objet sérialisé sous forme encodée en URL, au lieu de coder en dur la valeur renvoyée.
eduncan911
Cela fonctionne en partie pour moi. Des idées pourquoi j'obtiens Controller / au lieu de Controller / Action? Mon test échoue car ils ne sont pas tout à fait les mêmes et pourtant j'enregistre les mêmes valeurs de routage. Très étrange ...
Nick
3
La ApplyAppPathModifierpartie est le bit critique pour UrlHelper
Chris S
37

Une implémentation modifiée de eu-ge-ne. Celui-ci retourne un lien généré en fonction des routes définies dans l'application. L'exemple d'eu-ge-ne retournait toujours une réponse fixe. L'approche ci-dessous vous permettra de tester que l'action / le contrôleur et les informations de route corrects sont passés dans UrlHelper - ce que vous voulez si vous testez l'appel à 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
la source
12

Cet article peut être utile si vous souhaitez vous moquer de la classe HttpContextBase.

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

Gerardo Contijoch
la source
Cool, cela m'a aidé, même si j'ai dû ajouter du code supplémentaire à la méthode FakeHttpContext pour empêcher l'assistant d'exploser: context.Setup (ctx => ctx.Request.ApplicationPath) .Returns ("/ AntiBlowup"); J'ai également refactoré le code pour qu'il utilise la nouvelle syntaxe Setup (). Merci.
RichardOD
2

S'appuyant sur la réponse de @ eu-ge-ne qui m'a beaucoup aidé:

J'ai eu un ActionResult qui a fait une redirection ainsi qu'un appel UpdateModel avec un paramètre FormCollection. Pour que UpdateModel () fonctionne, je devais ajouter ceci à mon HttpRequestBase simulé:

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

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

Pour vérifier que l'URL redirigée était correcte, vous pouvez effectuer les opérations suivantes:

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