ASP.NET MVC: Unit-Test-Controller, die UrlHelper verwenden

170

Eine meiner Controller-Aktionen, die in einer Ajax-Anforderung aufgerufen wird, ist die Rückgabe einer URL an die Clientseite, damit diese eine Umleitung durchführen kann. Ich verwende Url.RouteUrl(..)und während meiner Unit-Tests schlägt dies fehl, da der Controller.UrlParameter nicht vorgefüllt ist.

Ich habe viele Dinge ausprobiert, unter anderem versucht zu stubben UrlHelper(was fehlgeschlagen ist), manuell ein UrlHelpermit RequestContexteinem zu stubben HttpContextBase(was bei einem RouteCollection.GetUrlWithApplicationPathAnruf fehlgeschlagen ist ).

Ich habe Google durchsucht, aber praktisch nichts zu diesem Thema gefunden. Mache ich etwas unglaublich Dummes Url.RouteUrlin meiner Controller-Aktion? Gibt es einen einfacheren Weg?

Um es noch schlimmer zu machen, möchte ich in der Lage sein, die zurückgegebene URL in meinem Komponententest zu testen. Tatsächlich bin ich nur daran interessiert zu wissen, dass sie auf die richtige Route umleitet, aber da ich eine URL anstelle von a zurückgebe Route möchte ich die URL steuern, die aufgelöst wird (z. B. mithilfe eines Stubbeds RouteCollection) - aber ich freue mich, wenn mein Test zunächst bestanden wird.

efdee
quelle

Antworten:

202

Hier ist einer meiner Tests (xUnit + Moq) nur für einen ähnlichen Fall (mit Url.RouteUrl im Controller)

Hoffe das hilft:

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
quelle
2
Vorerst habe ich mich für eine Lösung entschieden, bei der ich Anrufe an UrlHelper abstrahiert habe, damit ich sie abfangen kann. Vielen Dank für Ihr Snippet, es wird mir jedoch viel Zeit sparen, herauszufinden, wie man einen Request / Response / ControllerContext richtig verspottet.
Efdee
Danke für die Antwort @ eu-ge-ne, es hat mir auch sehr geholfen. Ich habe einige weitere MoQ-Setups hinzugefügt, um einen Formcollection-Parameter zu verwenden, der von UpdateModel
jebcrum
16
+1 ausgezeichnet. Ein Tipp: Ich verwende dies als MockHelper und ändere die Antwort.Setup für ApplyAppPathModifier in: response.Setup (x => x.ApplyAppPathModifier (Moq.It.IsAny <String> ()). Returns ((String url) ) => url); Es ist hässlich, aber ich bekomme das serialisierte Objekt in URL-codierter Form zurück, anstatt den zurückgegebenen Wert fest zu codieren.
eduncan911
Das funktioniert teilweise bei mir. Irgendwelche Ideen, warum ich Controller / anstelle von Controller / Action bekomme? Mein Test schlägt fehl, weil sie nicht ganz gleich sind und ich dennoch die gleichen Routing-Werte registriere. Sehr seltsam ...
Nick
3
Der ApplyAppPathModifierTeil ist das kritische Bit für den UrlHelper
Chris S
37

Eine modifizierte Implementierung von eu-ge-ne. Dieser gibt einen generierten Link zurück, der auf den in der Anwendung definierten Routen basiert. Das Beispiel von eu-ge-ne gab immer eine feste Antwort zurück. Mit dem folgenden Ansatz können Sie testen, ob die richtigen Informationen zu Aktion / Controller und Route an den UrlHelper übergeben werden. Dies möchten Sie, wenn Sie den Aufruf des UrlHelper testen.

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
quelle
12

Dieser Beitrag kann nützlich sein, wenn Sie die HttpContextBase-Klasse verspotten möchten.

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

Gerardo Contijoch
quelle
Cool, das hat mir geholfen, obwohl ich der Methode FakeHttpContext zusätzlichen Code hinzufügen musste, um zu verhindern, dass der Helfer in die Luft sprengt: context.Setup (ctx => ctx.Request.ApplicationPath) .Returns ("/ AntiBlowup"); Ich habe den Code auch überarbeitet, sodass er die neuere Setup () -Syntax verwendet. Vielen Dank.
RichardOD
2

Aufbauend auf der Antwort von @ eu-ge-ne, die mir sehr geholfen hat:

Ich hatte ein ActionResult, das eine Umleitung durchführte, sowie einen UpdateModel-Aufruf mit einem FormCollection-Parameter. Damit das UpdateModel () funktioniert, musste ich dies zu meiner verspotteten HttpRequestBase hinzufügen:

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

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

Um zu testen, ob die umgeleitete URL korrekt war, können Sie Folgendes tun:

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