Skip to content

Instantly share code, notes, and snippets.

@bryanmenard
Last active December 14, 2015 11:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bryanmenard/5080830 to your computer and use it in GitHub Desktop.
Save bryanmenard/5080830 to your computer and use it in GitHub Desktop.
RouteTestsBase - Base class for testing routes in ASP.NET MVC using action-centric tests
// Release under MIT license Copyright (c) 2013 Bryan Menard, http://www.bryblog.com/license.txt
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using NUnit.Framework;
namespace MyApp.Web.UnitTests.Routes
{
/// <summary>
/// Base class for route testing
/// </summary>
public class RouteTestsBase<TController> where TController : class, IController
{
private StubHttpContextForRouting Context;
private RouteCollection Routes;
private string RequestUrl;
private TController T4MvcController;
private readonly string ControllerName = GetControllerName();
[SetUp]
public void SetUpBase()
{
Routes = new RouteCollection();
RegisterRoutes(Routes);
T4MvcController = GetT4Controller();
SetUp();
}
private TController GetT4Controller()
{
const BindingFlags staticFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var controllerField = typeof(MVC).GetFields(staticFlags).SingleOrDefault(x => x.FieldType == typeof(TController));
if (controllerField != null)
{
return (TController)controllerField.GetValue(null);
}
// We need to check for Areas
// Find the eligible areas
var areas = typeof(MVC).GetProperties(staticFlags)
.Where(x => !typeof(IController).IsAssignableFrom(x.PropertyType)) // Find non-controller properties
.Select(x => x.GetValue(null))
.ToList();
var controller = areas.Select(x =>
{
var property = x.GetType().GetFields().SingleOrDefault(y => y.FieldType == typeof(TController));
if (property != null) return (TController)property.GetValue(x);
return (TController)null;
}).SingleOrDefault(x => x != null);
if (controller == null)
throw new InvalidOperationException("Unable to find T4MVC controller associated with this controller.");
return controller;
}
/// <summary>
/// Runs text-specific setup steps after base setup has executed
/// </summary>
protected virtual void SetUp() { }
private void RegisterRoutes(RouteCollection routes)
{
// Register areas
var areaTypes = typeof(MvcApplication).Assembly.GetTypes().Where(x => typeof(AreaRegistration).IsAssignableFrom(x)).ToList();
var areas = areaTypes.Select(x => (AreaRegistration)Activator.CreateInstance(x, nonPublic: true)).ToList();
foreach (var area in areas)
{
var areaContext = new AreaRegistrationContext(area.AreaName, routes);
area.RegisterArea(areaContext);
}
// Register main routes
MvcApplication.RegisterRoutes(routes);
}
protected void Url(string requestUrl)
{
if (!requestUrl.StartsWith("/")) requestUrl = "/" + requestUrl;
RequestUrl = requestUrl;
Context = new StubHttpContextForRouting(requestUrl);
}
private UrlHelper GetUrlHelper(string appPath = "/", RouteCollection routes = null)
{
if (routes == null)
{
routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
}
HttpContextBase httpContext = new StubHttpContextForRouting(appPath);
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "defaultcontroller");
routeData.Values.Add("action", "defaultaction");
RequestContext requestContext = new RequestContext(httpContext, routeData);
UrlHelper helper = new UrlHelper(requestContext, routes);
return helper;
}
protected T Ignore<T>()
{
return default(T);
}
protected void AssertRoute(Expression<Func<TController, ActionResult>> actionExpression)
{
var methodExpression = actionExpression.Body as MethodCallExpression;
if (methodExpression == null) throw new ArgumentException("The expression must be a method call to an Action.");
var routeData = Routes.GetRouteData(Context);
if (routeData == null) throw new InvalidOperationException("GetRouteData should return a non-null value.");
var method = methodExpression.Method;
Assert.That(ControllerName, Is.EqualTo(routeData.Values["controller"]));
var action = method.Name;
Assert.That(action, Is.EqualTo(routeData.Values["action"]));
var arguments = methodExpression.Arguments.Zip(method.GetParameters(), (arg, param) =>
{
object value;
bool ignore = false;
var constantArg = arg as ConstantExpression;
if (constantArg != null)
{
value = constantArg.Value;
}
else if (arg is MethodCallExpression)
{
ignore = true;
value = null;
}
else
{
throw new InvalidOperationException("The Action arguments must be a constant or a method call to Ignore.");
}
return new { Value = value, Name = param.Name, Ignore = ignore };
});
foreach (var argument in arguments.Where(x => !x.Ignore))
{
Assert.That(argument.Value.ToString(), Is.EqualTo(routeData.Values[argument.Name]), string.Format("Argument \"{0}\" does not have the correct value", argument.Name));
}
// Assert the outbound
var outboundAction = actionExpression.Compile().Invoke(T4MvcController);
var outboundUrl = GetUrlHelper(routes: Routes).Action(outboundAction);
Assert.That(outboundUrl, Is.EqualTo(RequestUrl));
}
private static string GetControllerName()
{
var name = typeof (TController).Name;
if (name.EndsWith("Controller"))
name = name.Substring(0, name.Length - "Controller".Length);
return name;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment