Denis Biondic
Denis Biondic

Reputation: 8201

Unit testing routing in ASP.NET Core 1.0 (ex MVC 6)

As the architecture of ASP.NET Core 1.0 (ex MVC 6 / ASP.NET 5.0) changed significantly, how would one go about unit testing the routing?

As an example, I like the libraries such as this one (as for <= MVC 5): https://github.com/AnthonySteele/MvcRouteTester

Something down the lines of fluent extension methods:

routes.ShouldMap("/").To<HomeController>(x => x.Index());

Upvotes: 28

Views: 6840

Answers (2)

Actually it is not quite hard to write your own route testing framework. I implemented route testing in MyTested.AspNetCore.Mvc and it works quickly and without any problem - https://github.com/ivaylokenov/MyTested.AspNetCore.Mvc

You can take a look at the code but basically, you need the following:

  1. Mocked IApplicationBuilder to extract registered routes from
  2. Custom HTTP RouteFeature to pass RouteData around
  3. Custom ControllerActionInvoker to extract the binded models and stop MVC from processing the action
  4. Test builders and expression parsers to actually prepare and execute the test

With these steps in mind, even tests like the following work correctly:

// action
public class NormalController : Controller
{
    [HttpPost]
    public IActionResult UltimateModelBinding(
        ModelBindingModel model, 
        [FromServices]IUrlHelperFactory urlHelper)
    {
        return null;
    }
}

// model
public class ModelBindingModel
{
    [FromBody]
    public RequestModel Body { get; set; }

    [FromForm(Name = "MyField")]
    public string Form { get; set; }

    [FromQuery(Name = "MyQuery")]
    public string Query { get; set; }

    [FromRoute(Name = "id")]
    public int Route { get; set; }

    [FromHeader(Name = "MyHeader")]
    public string Header { get; set; }
}

// unit test
MyMvc
    .Routes()
    .ShouldMap(request => request
        .WithLocation("/Normal/UltimateModelBinding/100?myQuery=Test")
        .WithMethod(HttpMethod.Post)
        .WithJsonBody(new
        {
            Integer = 1,
            String = "MyBodyValue"
        })
        .WithFormField("MyField", "MyFieldValue")
        .WithHeader("MyHeader", "MyHeaderValue"))
    .To<NormalController>(c => c.UltimateModelBinding(
        new ModelBindingModel
        {
            Body = new RequestModel { Integer = 1, String = "MyBodyValue" },
            Form = "MyFieldValue",
            Route = 100,
            Query = "Test",
            Header = "MyHeaderValue"
        },
        From.Services<IUrlHelperFactory>()));

P. S. Do not write such actions.

Upvotes: 4

Maxime Rouiller
Maxime Rouiller

Reputation: 13699

Alright... I did ping the ASP.NET team and asked them how they proceeded to do their tests.

Short answer

You can't unit test without mocking the world. You have to do integration/functional tests.

Slightly longer answer

Routing can come from many places (attributes, pre-defined routes, areas, etc) and can even be altered by middleware. Including all those scenarios in a test would need to setup some massive dependency tree while there is an easier way to test it.

How they do it.

Basically, they are creating a TestStartup.cs, self-hosting the app in the test process and hitting itself to see how it behaves. They override the results and... that's pretty much it.

I think I've given you all the possible tools here to actually bootstrap something for yourself.

Upvotes: 15

Related Questions