ASP.NET MVC: controller di unit test che utilizzano UrlHelper


170

Una delle azioni dei miei controller, che viene chiamata in una richiesta Ajax, sta restituendo un URL al lato client in modo che possa effettuare un reindirizzamento. Sto usando Url.RouteUrl(..)e durante i test delle mie unità questo non riesce poiché il Controller.Urlparametro non è precompilato.

Ho provato un sacco di cose, tra gli altri, che tentano di stub UrlHelper(che non), creando manualmente una UrlHelpercon una RequestContextche ha un contuso HttpContextBase(che non è riuscito in una RouteCollection.GetUrlWithApplicationPathchiamata).

Ho cercato su Google ma non ho trovato praticamente nulla sull'argomento. Sto facendo qualcosa di incredibilmente stupido Url.RouteUrlnell'azione del mio controller? C'è un modo più semplice?

A peggiorare le cose, vorrei essere in grado di testare l'URL restituito nel mio test unitario - in realtà sono interessato solo a sapere che sta reindirizzando sulla rotta giusta, ma dal momento che sto restituendo un URL anziché un percorso, vorrei controllare l'URL che è stato risolto (ad es. utilizzando uno stubbed RouteCollection) - ma sarò felice di far passare il mio test all'inizio.

Risposte:


202

Ecco uno dei miei test (xUnit + Moq) solo per un caso simile (usando Url.RouteUrl nel controller)

Spero che questo ti aiuti:

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
Per il momento ho scelto una soluzione in cui ho estratto le chiamate a UrlHelper in modo da poterle intercettare. Grazie per il tuo frammento, tuttavia, mi farà risparmiare un sacco di tempo a capire come deridere correttamente un Request / Response / ControllerContext.
efdee,

Grazie per la risposta @ eu-ge-ne, mi ha aiutato molto. Ho incluso alcune altre configurazioni moq per utilizzare un parametro formcollection utilizzato da UpdateModel
jebcrum,

16
+1 eccellente. Anche se un suggerimento: lo uso come MockHelper e cambio la risposta.Setup per ApplyAppPathModifier in questo: response.Setup (x => x.ApplyAppPathModifier (Moq.It.IsAny <String> ())). Returns ((String url ) => url); È brutto, ma ottengo l'oggetto serializzato in forma codificata url, invece di codificare il valore restituito.
eduncan911,

Questo funziona parzialmente per me. Qualche idea sul perché ottengo Controller / anziché Controller / Azione? Il mio test ha esito negativo perché non sono abbastanza uguali e tuttavia registro gli stessi valori di routing. Molto strano ...
Nick,

3
La ApplyAppPathModifierparte è il punto critico per l'UrlHelper
Chris S

37

Un'implementazione modificata da eu-ge-ne. Questo restituisce un collegamento generato in base alle route definite nell'applicazione. L'esempio di eu-ge-ne ha sempre restituito una risposta fissa. L'approccio seguente ti consentirà di verificare che l'azione / il controller e le informazioni sul percorso corretti vengano passati a UrlHelper, che è ciò che desideri se stai testando una chiamata a 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

Questo post può essere utile se vuoi deridere la classe HttpContextBase.

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


Bene, questo mi ha aiutato, anche se ho dovuto aggiungere un po 'di codice aggiuntivo al metodo FakeHttpContext per fermare l'esplosione dell'helper: context.Setup (ctx => ctx.Request.ApplicationPath) .Returns ("/ AntiBlowup"); Ho anche rifattorizzato il codice in modo che utilizzi la sintassi Setup () più recente. Grazie.
RichardOD,

2

Sviluppando la risposta di @ eu-ge-ne che mi ha aiutato moltissimo:

Ho avuto un ActionResult che ha effettuato un reindirizzamento e una chiamata UpdateModel con un parametro FormCollection. Perché UpdateModel () funzioni, ho dovuto aggiungere questo al mio HttpRequestBase deriso:

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

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

Per verificare che l'URL reindirizzato fosse corretto, è possibile effettuare le seguenti operazioni:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.