Reputation:
... but the same extension method works when the application itself is executing. The UrlHelper extension method itself looks like this:
public static string CategoryLandingPage(this UrlHelper helper, string seoCategoryName)
{
return helper.RouteUrl("Category", new { area = "SoAndSo", controller = "SoAndSo", action = "Category", seoCategoryName = seoCategoryName }, "http");
}
I register that particular route like this in my SoAndSoAreaRegistration class:
context.MapRoute(
"Category",
"category/{seoCategoryName}",
new { area = "SoAndSo", controller = "SoAndSo", action = "Category", seoCategoryName = string.Empty }
);
... and I have dropped a breakpoint on that registration to ensure that it gets hit by the test runner, and it does.
When I run the test, I get an ArgumentException, "A route named 'Category' could not be found in the route collection. Parameter name: name".
My guess is that we do not need to specify the route name and enough route parameters (area/controller/action/category name) to construct the route in its entirety as we're doing here, but I can't figure out where the route name disappears to during testing. Removing the category name eliminates the exception and allows the test to pass, but I would still like to understand where the route name disappears to when I'm testing. Simplifying the code like so still blows up at runtime:
public static string CategoryLandingPage(this UrlHelper helper, string seoCategoryName)
{
return helper.RouteUrl("Category");
}
If I dig through the route collection at runtime, I can find the category route, but there is no evidence of a .Name property, nor do I see the route's name ("Category" with a capital C) anywhere among the UrlHelper's properties (apologies for the goofy obfuscation; better safe than sorry):
Does anyone know how I can write unit tests which hit UrlHelper extension methods which reference routes by their name? Thanks!
Update - I'll add some of the test initialization, most of which I got from this popular question, lightly modified to account for the fact that the application I'm working with is separated into multiple MVC areas:
private SoAndSoController CreateController() { var service = new Mock(); var cookieMgr = new Mock(); var logger = new Mock();
var allRoutes = new RouteCollection();
MvcApplication.RegisterRoutes(allRoutes);
var soAndSoAreaRegistration = new SoAndSoAreaRegistration();
var soAndSoAreaRegistrationContext = new AreaRegistrationContext(soAndSoAreaRegistration.AreaName, new RouteCollection());
soAndSoAreaRegistration.RegisterArea(soAndSoAreaRegistrationContext);
soAndSoAreaRegistrationContext.Routes.ForEach(r => allRoutes.Add(r));
var request = new Mock<HttpRequestBase>();
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>();
response.Setup(x => x.ApplyAppPathModifier("/post1")).Returns("http://localhost/post1");
var context = new Mock<HttpContextBase>();
context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);
var controller = new SoAndSoController(service.Object, cookieMgr.Object, null, logger.Object, null);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), allRoutes);
return controller;
}
Upvotes: 1
Views: 759
Reputation:
I figured it out. I needed to pass my RouteCollection into the AreaRegistrationContext, rather than passing it a new RouteCollection:
var productFindingAreaRegistrationContext = new AreaRegistrationContext(productFindingAreaRegistration.AreaName, allRoutes);
But that was causing this line to blow up:
productFindingAreaRegistrationContext.Routes.ForEach(r => allRoutes.Add(r));
However now this line was no longer needed, so I commented it out. Voila.
Upvotes: 1