Tuesday, September 7, 2010

How to test ASP.NET MVC routines with optional parameters

I defined a map route in Global.asax.cs as below
routes.MapRoute(null,
"product/{category}",
new { controller = "Product", action = "List", category = UrlParameter.Optional });
and ProductController's List method
public ActionResult List(string category = null)
{
IList<Product> products;

if (category == null)
{
products = _productRepository.GetAll();
}
else
{
products = _productRepository.GetAllByCategory(category);
}

return View(products);
}
And here are a few attempts to test the route (with MVC Contrib Test Helper support)

First attempt


"~/product".Route().ShouldMapTo<ProductController>(c => c.List());
I have a compile error: "An expression tree may not contain a call or invocation that uses optional arguments". Grrrr, expressions don't play with optional arguments.

Second attempt


"~/product".Route().ShouldMapTo<ProductController>(c => c.List(null));
The test throws a MvcContrib.TestHelper.AssertionException: "Value for parameter 'category' did not match: expected 'System.Web.Mvc.UrlParameter' but was ''."

Third attempt


"~/product".Route().ShouldMapTo<ProductController>(c => c.List(""));
Same result as second attempt. Grrrr...

And the forth attempt


var routeData = "~/product".WithMethod(HttpVerbs.Get);
routeData.Values["category"] = null;
routeData.ShouldMapTo<ProductController>(c => c.List(null));
Now the test pass.

Why? 'category' is an optional parameter, as mentioned here, I expect that I don't need to explicit to define it in route value dictionary and the third attempt should be passed. Is a place to improve MVC Contrib test helper?

1 comment:

  1. Here is a little helper extension method that works well. You can now change your example to something like below.

    "~/product".Route()
    .SetOptionalParametersToNull()
    .ShouldMapTo(c=>c.List(null));

    public static class RouteExtenstions
    {
    public static RouteData SetOptionaltParametersToNull(this RouteData data)
    {
    var optionalParameters = data.Values.Where(keyValuePair => keyValuePair.Value == UrlParameter.Optional)
    .Select(x => x.Key).ToList();
    foreach (var optionalParameter in optionalParameters)
    data.Values[optionalParameter] = null;

    return data;
    }
    }

    ReplyDelete