简体   繁体   中英

Unit testing routing/controller in MVC4

Goal: test that a given url returns a given controller function.

In the process, I've broken into the routing system and I can't figure out how to test routes (or, for that matter, find the controller corresponding to the route :-/).

Sample code, which doesn't work:

[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);
}

edit: Using the linked blog post from Simon for the stub class.

[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);
}

All you are testing right now is if the routes are added to the collection, by accessing it by the route name, and not if the expected route will return given a virtual path. You need to obtain the route data as returned by the RouteCollection with a HttpContext.

Best way would be to use a mock or a stub for the HttpContext (or HttpContextBase) and call the RouteCollection's GetRouteData(HttpContextBase) method and inspect the route data.

There is a good example of this in Brad Wilson's blog: http://bradwilson.typepad.com/blog/2010/07/testing-routing-and-url-generation-in-aspnet-mvc.html

Edit:You cannot get a controller instance from the RouteData itself. However, RouteData should give you enough information to know which controller will be instantiated. For example, if you have a controller at MyProject.Controllers.HomeController with an action Home , this should hold true in your test (using xUnit and 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);

I've had prety good experience with MVCContrib's Testhelper

Take a look at this test of testhelper.

Saves a lot of hassles around stubbing HttpContext etc.

Also if you are on MVC4, have a look at this Nuget package which is a fork for MVC4.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM