C# MVC4中的单元测试路由/控制器

C# MVC4中的单元测试路由/控制器,c#,asp.net-mvc-4,C#,Asp.net Mvc 4,目标:测试给定的url是否返回给定的控制器函数 在这个过程中,我闯入了路由系统,我不知道如何测试路由(或者,就此而言,找到与路由对应的控制器:-/) 示例代码不起作用: [Test] public void kick_the_tires() { var rc = new RouteCollection(); Infrastructure.RouteRegistry.RegisterRoutes(rc); // get the route correspondi

目标:测试给定的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);
}

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

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

Brad Wilson的博客中有一个很好的例子:

编辑:无法从RoutedData本身获取控制器实例。然而,RouteData应该提供足够的信息,让您知道哪个控制器将被实例化。例如,如果您在
MyProject.Controllers.HomeController
中有一个控制器,并且有一个操作
Home
,那么这在您的测试中应该是正确的(使用xUnit和Moq):

//准备
var context=newmock();
var request=newmock();
var response=newmock();
var session=newmock();
var server=newmock();
SetupGet(c=>c.Request).Returns(Request.Object);
SetupGet(c=>c.Response).Returns(Response.Object);
SetupGet(c=>c.Session).Returns(Session.Object);
SetupGet(c=>c.Server).Returns(Server.Object);
request.SetupGet(r=>r.HttpMethod).Returns(“GET”);
request.SetupGet(r=>r.PathInfo).Returns(String.Empty);
SetupGet(r=>r.AppRelativeCurrentExecutionFilePath)。返回(“~/Home”);
var expectedHandler=typeof(HomeController).GetMethod(“Index”,Type.EmptyTypes);
var data=RouteTable.Routes.GetRouteData(context.Object);
Assert.NotNull(数据);
var handler=(MethodInfo)data.DataTokens[“actionMethod”];
相等(expectedHandler,handler);

我在这方面有很好的经验

看看这个

省去了stubing
HttpContext
等方面的大量麻烦


如果您使用的是MVC4,请查看哪个是MVC4的分支。

谢谢,Simon(欢迎使用StackOverflow!)。你能评论一下如何从一条路线上取回一个控制器吗,因为这正是我真正想做的吗?西蒙,这不起作用。我并不完全理解MVC4世界,但我相信DataToken应该包含路由中的参数,而不是被触发的方法;我编辑了我的示例以提供一个模拟HttpContext的示例,并修复了DataTokens字典的冗余。代码通过了我的单元测试(使用不同的控制器)。DataToken将包含传递给路由处理程序的任何额外属性。约束、名称空间等。。包含在值字典中。Simon,GetRouteData对于非索引查找一直返回null。我已经对它进行了一段时间的黑客攻击,并一直在寻找所谓的来源。有什么想法吗?我同意你的看法,测试任何需要HttpContext的东西都是非常痛苦的,因此为你做这项工作的库总是有用的。
// 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);