Integration testing ASP.NET 5 and ASP.NET MVC 6 applications

The other day I ran into a post by Alex Zeitler, who blogged about integration testing of ASP.NET MVC 6 controllers. Alex has done some great work for the Web API community in the past and I always enjoy his posts.

In this case, Alex suggested using self hosting for that, so spinning up a server and hitting it over HTTP and then shutting down, as part of each test case. Some people have done that in the past with Web API too, but is not an approach I agree with when doing integration testing. If you follow this blog you might have seen my post about testing OWIN apps and Web API apps in memory already.

My main issue with the self-host approach, is that you and up testing the underlying operating system networking stack, the so-called “wire” which is not necessarily something you want to test – given that it will be different anyway in production (especially if you intend to run on IIS). On the other hand, you want to be able to run end-to-end tests quickly anywhere – developer’s machine, integration server or any other place that it might be necessary, and doing it entirely in memory is a great approach.


In the past, to memory host a Web API application you’d use an HttpServer type, which was a part of the framework. To memory host an OWIN pipeline you could use Microsoft.Owin.Testing package or Damian Hickey’s OwinHttpMessageHandler.

In ASP.NET 5 (DNX world), Microsoft has produced a Nuget package called Microsoft.AspNet.TestHost which you can easily utilize to run your ASP.NET 5 in memory instead of over the wire.

In fact, the aforementioned OwinHttpMessageHandler is also DNX compatible now, but it operates on a lower sets of abstractions, such as AppFunc and MidFunc. However, if you are doing pure OWIN development, this is definitely something you’d want to check out.

On the other hand Microsoft.AspNet.TestHost allows you to plug in the Action<IApplicationBuilder> and Action<IServicesCollection> which will likely be already existing as part of your ASP.NET 5 Startup class.

Putting it together

To test an ASP.NET 5 application you will need the new XUnit.DNX integration packages and the Microsoft.AspNet.TestHost package. Here are my project.json dependencies (xunit Visual Studio integration for DNX projects is entirely contained in the *xunit.runner.dnx* package):

Next, let’s imagine a test controller and a default Startup class – of course in your application those will be much more complicated, but for illustration purposes the defaults that come with the MVC 6 project templates are enough. I also added a simple validation to one of the actions, just so that we have a code path with non-successful HTTP response code.

Microsoft.AspNet.TestHost.TestServer is the class that you will use, and it exposes a bunch of factory methods depending on how much set up you want to do:

The server is being created as http://localhost unless you set the BaseAddress property to something else (i.e. if you want https binding). The actual URL is not important and can be anything – as long as the server and client agree on the same value.

As mentioned before, the factory methods on the TestServer make it easy to integrate your Startup class into the tests.

Below is an example of the setup of tests:

All that’s left at this point is to simply create the instance of a TestServer in each individual test, pass in the delegates saved in the fields, and create an HttpClient off the server (TestServer exposes a method CreateClient). At that point, you use the instantiated HttpClient just like you are used to – except all interaction with the “server” happens in memory.

Below are a few example tests against our test controller shown earlier:

And the result:

Integration tests

In each of the above cases we are able to run robust end-to-end integration in a very fast way, without worrying about the network stack.

What’s worth mentioning is that all end-to-end tests in the MVC 6 repository use the same approach.

Be Sociable, Share!

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1864()

  • Pingback: Dew Drop – May 20, 2015 (#2018) | Morning Dew()

  • PK

    given -> public Tests()
    should be -> public IntegrationTests() ? (that’s a ctor, right?)

    • Filip W

      yeah my bad

  • John Davidson

    This is exactly what I need for test a WebAPI where Dependency Injection depends on the startup process. However the transition from beta4 to beta5 changed how the startup operates. Do you have an updated version for beta5?

    (All these breaking changes make me think I am dealing with a late stage alpha release rather than an early beta.)

  • Shederman

    When I try and use xunit.runner.aspnet I get:

    System.MissingMethodException: Method not found: ‘Boolean Microsoft.Framework.Runtime.Project.TryGetProject(System.String, Microsoft.Framework.Runtime.Project ByRef, System.Collections.Generic.ICollection`1)’.

    Would love to use xunit.runner.dnx, but it just plain doesn’t exist.

    As with everything else in AspNet50

  • Utter Knob

    Nice post in what seems a void for useful documentation on how to integrate xunit with asp 5. As per previous comment the above code does not build with beta6 so an update is required.

  • Israel García

    At least in beta7 the problem is that after all the adjusting of methods signature changes, enviroment webrootpath is pointing within the test project, not the original web project, so views are not founds.

    • Scott Wylie

      Did you ever figure out how to change the webrootpath in your tests to point to the api project?

  • Todd Hitt

    This is exactly what I need! But, I’ve got one problem: I can’t figure out how to access CallContextServiceLocator. I get an error that it doesn’t exist in the current context.

    Any hints?

    I’m using 1.0.0-beta7 but figure I’m missing some sort of dependency in project.json or a using statement.

  • Ивайло Кенов

    Thanks for the useful post! :)
    Any plans for a blog post about route testing in ASP.NET MVC 6?

  • Eirik k

    Great post, sadly as others have pointed out, it does no longer build. An updated version would be awesome.