Integrating the #WebAPI HttpClient and ApiController in a single test

In the two previous blog posts I showed how to unit test and ASP.NET WebAPI controller and how to unit test the client side code depending on the HttpClient class. Both unit tests are perfectly valid but as so often just adding unit tests can be deceptive. After all testing an ApiController by just calling the methods makes it perfectly possible to call them in such a way that would never be possible using a real HTTP request. So in order to complete out testing we should test the integration of the client and server parts using an integration test.


Integration testing with the ASP.NET WebAPI

Creating a complete integration test with the complete HTTP stack is possible but adds rather a lot of complexity to our test harness. It would be much nicer if we could just test the client and server part together without actually having to use an HTTP server. Turns out that this can be done quite easy because the HttpClient can use an HttpMessageHandler as the messaging pipeline and the HttpServer derives from that same HttpMessageHandler. When testing the client code in the previous blog post we already used this fact. However there we added a custom TestingDelegatingHandler<T> to return a fake result message. Instead of doing that we can also just use the real server and configuration and this will create an instance of the ApiController and route the request to that instance. 


Testing the getting of books

The code used to test getting the books from the ApiController is actually quite similar to testing just the client code. The main difference is that the actual controller is instantiated and called so make sure that returns the result to test for. In this case it uses an in memory repository so I know exactly that the same set of 5 books are returned every time.

   1: [TestMethod]

   2: public void WhenGettingItShouldReturnAllBooks()

   3: {

   4:     // Arrange

   5:     var config = new HttpConfiguration();

   6:     WebApiConfig.Register(config);

   7:     var server = new HttpServer(config);

   8:     var client = new BooksClient(new HttpClient(server));


  10:     // Act

  11:     var books = client.GetBooks();


  13:     // Assert

  14:     Assert.AreEqual(5, books.Count());

  15: }

Nice and simple and still it uses the almost complete WebAPI stack including the routing.


The test of adding a new book

Adding a new book in an integration test is just as simple:

   1: [TestMethod]

   2: public void WhenPostingABookItShouldBeAdded()

   3: {

   4:     // Arrange

   5:     var config = new HttpConfiguration();

   6:     WebApiConfig.Register(config);

   7:     var server = new HttpServer(config);

   8:     var client = new BooksClient(new HttpClient(server));


  10:     var book = new Book { Title = "A new book", Author = "Maurice" };


  12:     // Act

  13:     var response = client.PostBook(book);


  15:     // Assert

  16:     var newBook = response.Item1;

  17:     Assert.AreEqual(book.Title, newBook.Title);

  18:     Assert.AreEqual(book.Author, newBook.Author);


  20:     Assert.AreEqual(string.Format("http://localhost:63895/api/books/{0}",

  21:         newBook.Id), response.Item2.ToString());

  22: }


Adding message serialization to the integration tests

Both these test do a pretty good job of integrating the client and server parts for a quick in memory test but still don’t test message serialization and de-serialization. Adding the serialization part to an integration test would make it even more useful. Fortunately with the excellent work by Kiran Challa found in this blog post and the InMemoryHttpContentSerializationHandler listed there is is easy enough to add. The following test used this InMemoryHttpContentSerializationHandler to test if updating an unknown book throws the expected exception. The serialization part is no problem here but it could have been πŸ˜‰

   1: [TestMethod]

   2: [ExpectedException(typeof(HttpRequestException))]

   3: public void WhenPuttingAnInvalidBookItShouldThrow()

   4: {

   5:     // Arrange

   6:     var config = new HttpConfiguration();

   7:     WebApiConfig.Register(config);

   8:     var server = new HttpServer(config);


  10:     var handler = new InMemoryHttpContentSerializationHandler(server);

  11:     var client = new BooksClient(new HttpClient(handler));



  14:     var book = new Book { Id = int.MaxValue, Title = "A non existent book", Author = "Maurice" };


  16:     // Act

  17:     client.PutBook(book);


  19:     // Assert

  20:     Assert.Fail("Should not get here.");

  21: }


Sweet πŸ™‚

Leave a Reply

Your email address will not be published. Required fields are marked *