ASP.NET MVC:使用UrlHelper的单元测试控制器


170

我的一个控制器操作(在Ajax请求中被调用的操作)正在将URL返回给客户端,以便它可以进行重定向。我正在使用,Url.RouteUrl(..)并且在我的单元测试期间由于Controller.Url参数未预先填写而失败。

我尝试了很多尝试,其中包括尝试存根UrlHelper(失败),手动创建UrlHelper带有RequestContext存根HttpContextBase(在RouteCollection.GetUrlWithApplicationPath通话中失败)的。

我已经搜索过Google,但实际上没有找到任何相关信息。我在执行Url.RouteUrlController动作时是否正在做一些非常愚蠢的事情?有更容易的方法吗?

更糟糕的是,我希望能够在单元测试中测试返回的URL-实际上,我只想知道它已重定向到正确的路由,但是由于我返回的是URL,而不是路由,我想控制解析的URL(例如通过使用stubbed RouteCollection)-但我很乐意让我的测试通过。

Answers:


202

这是我针对类似情况的测试(xUnit + Moq)之一(在控制器中使用Url.RouteUrl)

希望这可以帮助:

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
暂时,我提供了一个解决方案,其中提取了对UrlHelper的调用,以便可以拦截它们。但是,感谢您的摘录,它将为我节省大量时间来弄清楚如何正确模拟Request / Response / ControllerContext。
efdee

感谢您提供@ eu-ge-ne的答案,它也帮了我很多忙。我包括了更多的最小起订量设置,以使用UpdateModel使用的formcollection参数
jebcrum,2009年

16
+1好。虽然有一个提示:我将其用作MockHelper并将ApplyAppPathModifier的response.Setup更改为:response.Setup(x => x.ApplyAppPathModifier(Moq.It.IsAny <String>()))。Returns((String url )=>网址); 这很丑陋,但是我以url编码的形式返回了序列化的对象,而不是硬编码返回的值。
eduncan911

这部分对我有用。有什么想法为什么我要使用Controller /而不是Controller / Action?我的测试失败,因为它们并不完全相同,但是我注册了相同的路由值。很奇怪...
尼克

3
ApplyAppPathModifier部分是UrlHelper的关键部分
Chris S

37

eu-ge-ne的改进实现。这将根据应用程序中定义的路由返回生成的链接。eu-ge-ne的示例始终返回固定的响应。下面的方法将允许您测试是否将正确的操作/控制器和路由信息传递到UrlHelper中-如果要测试对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

如果您想模拟HttpContextBase类,那么这篇文章可能会很有用。

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


太酷了,这对我有所帮助,尽管我不得不在FakeHttpContext方法中添加一些额外的代码以阻止辅助程序崩溃:context.Setup(ctx => ctx.Request.ApplicationPath).Returns(“ / AntiBlowup”); 我还重构了代码,以便它使用更新的Setup()语法。谢谢。
RichardOD

2

通过@ eu-ge-ne建立答案,这对我有很大帮助:

我有一个执行重定向的ActionResult以及一个带有FormCollection参数的UpdateModel调用。为了使UpdateModel()正常工作,我必须将其添加到模拟的HttpRequestBase中:

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

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

要测试重定向的URL是否正确,您可以执行以下操作:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.