在MVC4中单元测试路由/控制器

5

目标:测试给定的url是否返回给定的控制器函数。

在这个过程中,我已经进入了路由系统,但是我不知道如何测试路由(或者说,找到对应于路由的控制器 :-/)。

以下是示例代码,但并不能正常工作:

[Test]       
public void kick_the_tires()
{
   var rc = new RouteCollection();

   Infrastructure.RouteRegistry.RegisterRoutes(rc);

   // get the route corresponding to name.
   var got = rc["name"];

   var expected = //What? foo is an internal type that can't be instantiated.

   Assert.AreEqual(foo, frob);
}

编辑:使用Simon的博客文章中提供的存根类。
[TestCase("/", "~/", "Home", "Index")]
[TestCase("/", "api/command", "Other", "command")]
internal void stub_mocker(string apppath, string route, string expected_controller,\
  string expected_action)
{
  var rc = new RouteCollection();
  Infrastructure.RouteRegistry.RegisterRoutes(rc);

  var httpmock = new StubHttpContextForRouting(
      appPath: apppath,
      requestUrl: route);

  // this always returns null for everything but the Index case.
  var routeData = rc.GetRouteData(httpmock);

  var controller = routeData.Values["controller"];
  var action = routeData.Values["action"];
  Assert.AreEqual(expected_controller, controller);
  Assert.AreEqual(expected_action, action);
}
2个回答

5

您现在测试的只是路由是否按名称添加到集合中,而不是在给定虚拟路径的情况下返回预期路由。您需要使用HttpContext获取RouteCollection返回的路由数据。

最好的方法是使用HttpContext的模拟或存根(或HttpContextBase),并调用RouteCollection的GetRouteData(HttpContextBase)方法并检查路由数据。

Brad Wilson的博客中有一个很好的例子: http://bradwilson.typepad.com/blog/2010/07/testing-routing-and-url-generation-in-aspnet-mvc.html

编辑:您无法从RouteData本身获取控制器实例。但是,RouteData应该为您提供足够的信息,以了解将要实例化哪个控制器。例如,如果您有一个控制器位于MyProject.Controllers.HomeController中,并且具有Home操作,则这应该在您的测试中成立(使用xUnit和Moq):

// Prepare
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.SetupGet(c => c.Request).Returns(request.Object);
context.SetupGet(c => c.Response).Returns(response.Object);
context.SetupGet(c => c.Session).Returns(session.Object);
context.SetupGet(c => c.Server).Returns(server.Object);
request.SetupGet(r => r.HttpMethod).Returns("GET");
request.SetupGet(r => r.PathInfo).Returns(String.Empty);
request.SetupGet(r => r.AppRelativeCurrentExecutionFilePath).Returns("~/Home");


var expectedHandler = typeof (HomeController).GetMethod("Index", Type.EmptyTypes);

var data = RouteTable.Routes.GetRouteData(context.Object);

Assert.NotNull(data);

var handler = (MethodInfo) data.DataTokens["actionMethod"];
Assert.Equal(expectedHandler, handler);

Simon,这个不好使。我并不完全掌握MVC4的世界,但我认为DataToken应该包含从路由中获取的参数而不是被触发的方法。 - Paul Nathan
我不太确定你所说的非索引查找是什么意思。你能否更新一下你的问题,并提供你正在使用但无法正常工作的代码? - Simon Belanger
注意。我相当确定发生的情况是GetRouteData没有正确匹配我传入的URL。这可能是因为路由注册表本身使用MapHttpRoute,而索引路由注册例程使用MapRoute。使用调试器检查rc中的不同路由时,其中肯定存在不同类型。 - Paul Nathan
Simon: ~/api/command 出现了未实现的错误(这很奇怪,因为它应该存在)。目前应该全部是 GET。 - Paul Nathan
@bkwdesign:两年多过去了,我无法回答这个问题,因为自从这个项目以来我就没有使用过MVC4。 - Paul Nathan
显示剩余9条评论

1

我同意你的观点,测试任何需要HttpContext的东西都很痛苦,因此一个可以为你完成这项工作的库总是很有用的。 - Simon Belanger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接