ASP.NET Web API integration testing with in-memory hosting - StrathWeb

Strath

ASP.NET Web API integration testing with in-memory hosting

Because lightweight in-memory server is what the doctor ordered

In-memory hosting is one of the hidden gems of ASP.NET Web API. While the community, forums, bloggers have been buzzing about web-host and self-host capabilities of Web API, aside from the terrific post by Pedro Felix, very little has been said about in memory hosting.

Let me show you an example today, how a lightweight Web API server can be temporarily established in memory (without elevated priviliges, or cannibalizing ports like self host) and used to perform integration testing, allowing you to test almost the entire pipeline, from the request to the response.

Intro to in memory hosting

In memory hosting comes down to creating an instance of HttpServer class. HttpServer itself is derived from HttpMessageHandler, and can be treated like one. It allows you to perform direct communication between the server runtime, and the client side, through the use of HttpClient or HttpMessageInvoker. As soon as the purpose for the existance of the server is fulfilled, you can very easily dispose of it.

As mentioned, the main advantage of this over self-host, is that it doesn’t require a port or a base address; since by design it doesn’t run on localhost, but truly in memory. You can pretty much mock the entire server environment/runtime in memory!

All this makes in-memory hosting incredibly useful and powerful tool for various testing scenarios, especially for integration testing. It allows you to quickly test the entire processing pipeline of your server, including controllers, filters/attributes, message handlers and formatters. From the moment the HTTP request leaves the client, all the way to the point when the response is received.

We will use integration testing as an example of application of in-memory hosting. A disclaimer here: it will by no means be fullly-fledged, production-ready testing sample or methodology walkthrough; rather my focus here is to show how you can leverage on Web API in-memory hosting to facilitate integration testing scenarios.

My test application

In my test application, I would like to test the following things:
– submitting a request
– a message handler
– custom action filter attribute
– controller action
– JSON formatter applied to the incoming response
– receiving the response

I will scrape the application together from the various pieces of code I have been blogging about over the weeks. The repository will come from this post, the filter will be a simplified version of the caching filter from here, and the handler will be the api key handler from here.

The handler will check for an “apikey”, if the request doesn’t have it, then error will be returned.

The filter’s role will be to add cache control directives to the response. That is what I will be checking for in the tests.

Setting up test project

My favorite testing framework is xUnit, but obviously any one would do here. Let’s add the test project then, download xUnit and get going.

I will set up the server in the test class’ constructor.

The configuration of the in-memory Web API host is almost exactly the same as configuring web- or self- host. You need specify, at the very least, the routes to be used, and that is done through the familiar HttpConfiguration. Server is instantiated by passing the configuration to the HttpServer constructor.

Notice how I wire up the Message Handler which I will be wanting to test in the server’s configuration. The server’s instance itself will be a private property of the class and will be reused between different test methods.

Since our test class will implementIDisposable, we need to cater for the Dispose method:

Now, what I will be testing against is:
– get all items from the repository via GET
– get single item from the repository via POST
– remove single item from the repository via POST
– get all items from the repository via GET without API key (fail on purpose)

In each case we will be starting off by constructing the HttpRequestMessage, sending it to our in memory server, and receiving its response in the form of HttpResponseMessage, which will be used for all kinds of Asserts. What’s important to re-iterate here, this is all simulating the normal end-to-end behavior of the Web API – so in other words a great integration test.

I will be using HttpClient‘s SendAsync method for sending data to the server, since it accepts HttpRequestMessage, rather than POCO objects, so would allow us to simulate the behavior of the browser.

Before proceeding, I will add up two private helpers, which will be used for constructing HttpRequestMessages to be sent to the server – one for generic HttpRequestMessage and one with an ObjectContent.

Get all items

So we create the HttpClient and pass our server to it, as if we were passing the DelegatingHandler – how cool is that? I have an expected JSON response, in the string form, which I will use to compare with the actual result. Then we have a bunch of other asserts:
– response not null
– response content type is “application/json”
– object extracted from the response, IQueryable of Url, contains 3 items (to check if the apikey handler didn’t stop the request)
– two CacheControl asserts (to check if our ActionFilter works)
– comparison to the the expected Json (to check if JSON formatter works)

So, in a few lines of simple code, we have tested so much of the actual, real world functionalities.

Get single item

This is very similar to the one before, except this time we will be submitting an object. I instantiate Url object, and send it to the controller. I can serialize the object using JSON.NET and compare the output (the controller returns the added object) – both by reading the content as string and comparing to JSO.NET serialized variable, and by reading the underlying type from the response.

Remove single object

In this test, I will be removing an item from the repository. This is done by posting an item ID as integer. The controller returns the deleted object instance. Again, I can compare the JSON output to the expected one, as well as extract the underlying POCO object and compare that (in this case I’m just comparing its ID).

Request without apikey

This last test is an expected failure, as we make a request without providing the apikey. We can verify that the handler works just fine, and that the returned response is a denial of access (403 code).

Running the test

We can now compile the test project, and run the tests in xUnit. Since I am allergic to consoles, let’s use the GUI.

As you can see all the tests pass like a charm. This helps us to verify that it’s not just our unit logic that’s correct, but the entire pipeline behaves like it should. In a few simple methods, we have managed to test so much, by working only with HTTP request and HTTP response; all thanks to in memory hosting.

Summary and source code

As mentioned before, to me in memory hosting is one of the hidden gems of ASP.NET Web API. You should really take advantage of this awesome funcionality. It takes unit/integration testing to new heights, allowing you to produce even more robust applications.

Till next time!

source code is on github, as always.

Be Sociable, Share!

  • Pingback: Dew Drop – June 13, 2012 (#1,345) | Alvin Ashcraft's Morning Dew

  • http://kiwidev.wordpress.com Shaun

    I didn’t realize this existed. Awesome writeup. Thanks

    • Mande

      In awe of that answer! Really cool!

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1124

  • http://www.dotnettechy.com Micle

    Great article, its very useful

    We can also submit our .net related website and blog on dotnet directory to improve traffic.
    http://www.dotnettechy.com/dotnet-directory-list.aspx

    We will get strong relevant back-link.

  • http://antix.co.uk Anthony Johnston

    Thanks for the write up, I am more impressed every day with webapi and now how testable it is.

    This is worth a mention, being able to set a dependency resolver for in-memory tests

    var config = new HttpConfiguration
    {
    DependencyResolver = new MyTestResolver()
    };

    coool, thanks again, Ant

  • RBrandwood

    Great write up, just what I’ve been looking for.
    Is there any way to use the route configurations from the project under test with the in-memory server?

    I have a number of route / controller configurations defined for a REST api and I’d like to be able to use them directly when configuring the HttpServer without having to define them separately for an intergration test project.

  • http://www.timothy-giedner.com Tractor Tim

    great page mate

  • Curious Chap

    great stuff mate

  • Paul Manzotti

    Great article, and thanks for the one on setting up Web API with Ninject too. Which leads me nicely to my question: Do you have any idea how you can set up the in-memory server with Ninject for testing? I’m using the Ninject Mocking extension, as my Controllers are relying on DI to get their repositories. I’ve re-used the classes from your previous article, and I’ve put the code from the Start method into the test SetUp method, and Stop into the TearDown method.

    Not sure what I’m doing wrong, but I get 404s when trying to get to a controller. Any suggestions would be greatly appreciated!

    • Filip W

      Thanks Paul that’s really nice to hear. To be honest I haven’t used Ninject mocking with in-mem hosting yet. When I find some free time this week, will look into it!

      • Paul Manzotti

        Thanks! I’ll keep plugging away at it, so I’ll post here if I work it out. I guess that it’s the difference between the StandardKernel and the MoqMockingKernel. It probably doesn’t help that both Web API and Ninject Mocking are pretty new to me, so don’t have an in-depth understanding of them both!

      • Paul Manzotti

        No luck so far, but I’ve just posted a question on SO, with more detail and some code that might make more sense!

      • Paul Manzotti

        I’ve figured it out, and the MockingKernel was a red herring! I ended up setting up a normal Ninject Kernel, and binding the interface that I wanted to mock using ToConstant() and binding it to a Moq object I created. So the plumbing that ASP.Net created to route the calls to the correct controller is created.

        • Filip W

          ah! I totally forgot about that. I mean I had it in the back of my mind that I was supposed to do something with Web API testing, so I ended up writing this post :)

          But it kind of makes sense, I mean I had a quick look at MockingKernel and (after a quick glance) it seems that if you use that it will auto-mock all interfaces you don’t explicitly specify and thus kill the controller selector, action selector and as a result Web API routing.

          • Joe

            I am still trying to make it working but to no avail..any help would be much appreciated.

            [TestMethod]
            public void TestMethod1()
            {
            var config = new HttpConfiguration();
            config.Routes.MapHttpRoute(
            name: “DefaultApi”,
            routeTemplate: “api/{controller}/{id}”,
            defaults: new { id = RouteParameter.Optional });
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
            var container = Bootstrapper.Initialize();
            config.DependencyResolver = new StructureMapResolver(container);

            _server = new HttpServer(config);

            var client = new HttpClient(_server);

            var request = createRequest(“api/VehicleInfo/12/”, “application/xml”, HttpMethod.Get);

            using (HttpResponseMessage response = client.SendAsync(request).Result)
            using (HttpResponseMessage response = client.GetAsync(“http://localhost:8080/api/VehicleInfo/12/”).Result)
            {
            Assert.IsNotNull(response.Content);
            }

            request.Dispose();
            }

            private HttpRequestMessage createRequest(string url, string mthv, HttpMethod method)
            {
            var request = new HttpRequestMessage();

            request.RequestUri = new Uri(_url + url);
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mthv));
            request.Method = method;

            return request;
            }

  • Gladio Gent

    hey great job

  • joe

    Only came across your site last week, and I keep finding more really good bits of stuff to do with webapi. Nice work!

    • Filip W

      Thanks, really appreciate it! Also, sorry if some stuff doesn’t work, many of the posts were written against Web API RC and some against Beta. Trying to find time to update all the posts so that they play nicely with RTM :)

  • Taisha Farish

    Very interesting details you have observed, regards for posting.

  • Pingback: Doing unit and integration tests with the Web API HttpClient | Pablo Cibraro's blog

  • Pingback: ASP.NET Web API Testing | Jonathan Channon's Blog

  • Pingback: ASP.NET Web API In-Memory Hosting Integration Test with Ninject

  • Pingback: ASP.NET Web API In-Memory Hosting Integration Test and Ninject | Notebook Heavy

  • http://twitter.com/samiovaska Sami Ovaska

    Excellent article, thanks! This was just what I needed!

    Do you have any idea how support for authorization could be added to in-memory integration testing? I know that it’s possible to set credentials when creating HttpClient, but it’s not possible since HttpServer is given as parameter for HttpClient in this scenario:

    var credCache = new CredentialCache();
    credCache.Add(new Uri(“http://.com/”),”Digest”, new NetworkCredential(UserName,SecurelyStoredPassword));
    var httpClient = new HttpClient( new HttpClientHandler { Credentials = credCache});
    var response = httpClient.GetAsync(new Uri(“http://requestUri”));

  • ravennasoftware

    This is very cool…is there an easy way to debug into the actual service from your unit tests?

  • Abhijeet

    Nice writeup. Were you able to get HttpConfiguration to work with AttributeRouting?
    Basically, when I try to register routes via the “MapHttpAttributeRoutes()” extension method provided by AttrbuteRouting I get the following error “The constraint entry ‘inboundHttpMethod’ on the route with route template ‘api/Contacts/{id}’ must have a string value or be of a type which implements ‘IHttpRouteConstraint’.”

    Any thoughts? I have posted this on SO as well.

    http://stackoverflow.com/questions/18778390/attributerouting-not-working-with-httpconfiguration-object-for-writing-integrati

  • Dennis Doomen

    That’s an interesting approach. Do you see testing like this as an extra layer around the individual unit tests, or do you only test at this scope? I would also propose to use a proper assertion library that provides the necessary details about a failure to prevent any debugger sessions. For instance Fluent Assertions.

  • Tristan Blackwell

    Hey Filip, another great article, thanks! I’m just looking at implementing these types of test atm, and I’ve noticed that the ReadAsStringAsync method doesn’t apply any of the formatters defined in the GlobalConfiguration.Configuration.Formatters – As I’m using these tests to also test that the Json returned is exactly as expected (And I’m changing the order of properties and their casing), is there a way to apply my JsonFormatter to the returned Json without the de-serialisation/serialisation to an object within the test?

  • Pingback: Web Api – Integration Testing | The Inner Donkey Sanctuary