RouteEvaluator For Unit Testing Routes

A while back I wrote a routing debugger which is useful for testing your routes and seeing which routes would match a given URL. Rob suggested we have something like this for unit tests, so I whipped something simple up.

This is a class that allows you to test multiple different URLs quickly. You simply create the RouteEvaluator giving it a collection of routes and then GetMatches which returns a List<RouteData> containing a RouteData instance for every route that matches, not just the first one.

Here's a sample of usage.

[Test]
public void CanMatchUsingRouteEvaluator()
{
  var routes = new RouteCollection();
  GlobalApplication.RegisterRoutes(routes);

  var evaluator = new RouteEvaluator(routes);
  var matchingRouteData = evaluator.GetMatches("~/foo/bar");
  Assert.IsTrue(matchingRouteData.Count > 0);
  matchingRouteData = evaluator.GetMatches("~/foo/bar/baz/billy");
  Assert.AreEqual(0, matchingRouteData.Count);
}

And here’s the code. Note that my implementation relies on Moq, but you could easily implement it without using Moq if you wanted to.

public class RouteEvaluator
{
  RouteCollection routes;
    
  public RouteEvaluator(RouteCollection routes)
  {
    this.routes = routes;
  }

  public IList<RouteData> GetMatches(string virtualPath)
  {
    return GetMatches(virtualPath, "GET");
  }

  public IList<RouteData> GetMatches(string virtualPath, string httpMethod)
  {
    List<RouteData> matchingRouteData = new List<RouteData>();

    foreach (var route in this.routes)
    {
      var context = new Mock<HttpContextBase>();
      var request = new Mock<HttpRequestBase>();

      context.Expect(ctx => ctx.Request).Returns(request.Object);
      request.Expect(req => req.PathInfo).Returns(string.Empty);
      request.Expect(req => 
req.AppRelativeCurrentExecutionFilePath).Returns(virtualPath);
      if (!string.IsNullOrEmpty(httpMethod))
      {
        request.Expect(req => req.HttpMethod).Returns(httpMethod);
      }

      RouteData routeData = this.routes.GetRouteData(context.Object);
      if (routeData != null) {
        matchingRouteData.Add(routeData);
      }
    }
    return matchingRouteData;
  }
}

Let me know if this ends up being useful to you.

Technorati Tags: ,,
[ad] Free Bug Tracking & Project Management Software Axosoft’s OnTime 2007 allows software development teams to collaborate on software projects by tracking everything from defects to enhancements to helpdesk incidents in one easy-to-use database driven by an intuitive Windows, Web or VS.NET Integrated UI. Get a Free Single-User License ($200 Value!)

What others have said

Requesting Gravatar... Rob Conery May 05, 2008 5:45 PM
# re: RouteEvaluator For Unit Testing Routes
OMFG I love you!
Requesting Gravatar... Vijay Santhanam May 05, 2008 7:27 PM
# re: RouteEvaluator For Unit Testing Routes
nice! you're a Moq convert aren't you? me too.
Requesting Gravatar... Simone Busoli May 06, 2008 12:51 AM
# re: RouteEvaluator For Unit Testing Routes
Very cool Phil, that's something I didn't think about, but looks very useful.
Requesting Gravatar... Pete May 06, 2008 4:49 AM
# re: RouteEvaluator For Unit Testing Routes
What the heck was that "GlobalApplication"?! Design-disaster and naming disaster.

I've read somewhere "no global objects in OO". And the name GlobalApplication doesn't really describe anything. What is a NonGlobalApplication? Or LocalApplication.

Crazy stuff. Otherwise it's all good.
Requesting Gravatar... Simone Busoli May 06, 2008 5:08 AM
# re: RouteEvaluator For Unit Testing Routes
Pete, it's just the name of the class behind Global.asax, nothing weird ;)
Requesting Gravatar... Troy DeMonbreun May 06, 2008 10:04 AM
# re: RouteEvaluator For Unit Testing Routes
I'm presuming you guys wouldn't literally write tests that had a proliferation of URLs to verify?

I.e., that the above unit test is just a simplified example and it would be better practice to write a much less brittle unit test, e.g.: reference an external file containing the URLs (and possibly other metadata) and write the test to iterate the file and output a global pass/fail (with maybe summary text of the URLs that failed)?

Requesting Gravatar... tgmdbm May 06, 2008 8:23 PM
# re: RouteEvaluator For Unit Testing Routes
Love it. Nice one Phil.

@Troy, yeah, I think using a csv file with url,routeName,failureMessage is a nice idea. Then you can write one test to rule them all. But this is really down to your own preference, and might not be applicable in certain projects.

My only problem with testing urls is I don't really care what my urls look like, read: "I have no *good* reason to care". As long as when i call ActionLink passing in whatever values, it generates a url such that requesting that url will pass those values to my controller action. If that is true then I'm happy.

So what I do is exactly that. I get the url from GetVirtualPath, and pass it back through GetRouteData and Assert that what i get out is what i put in. I don't test the url directly.
Requesting Gravatar... Steve May 07, 2008 2:36 AM
# re: RouteEvaluator For Unit Testing Routes
Has anyone managed to get the code to work correctly?

Besides the fact that it doesn't compile (Calls to GetMatchingRoutes should be changed to GetMatches, or vice-versa), it always returns as many routes as I have registered in my RouteCollection, and they're always excatly the same routes! It looks like the foreach in GetMatches is redundant, but then I can't see how it can find multiple routes that match the same rule?

Also, is there any way to find out whether the Controller returned for a route exists?
Requesting Gravatar... Haacked May 07, 2008 8:08 AM
# re: RouteEvaluator For Unit Testing Routes
@Steve sorry, i had some typos in there. I fixed them up.

What do you have to say?

(will show your gravatar)
Please add 8 and 7 and type the answer here: