CHAPTER 12
Like ASP.NET MVC, ASP.NET Web API was written with testability in mind. This means that we can develop REST services using test-driven development, or just add unit tests or integration tests to our existing applications. If we take a look at the ASP.NET source code, the main solution contains more than 1,000 tests—Microsoft is serious about testing.
One of the pillars of testability is the decoupling of the elements that are being tested. In our case this means that the controllers should be decoupled from other components. We can only write test units with decoupled controllers; if we can’t get the complete decoupling, we can write only integration tests.
Consider the following controller with just three actions:
How can we unit test this controller?
It’s impossible since it has a strong dependency on the repository class and since the controller creates the instance of the repository inside the constructor. The new operation is what really couples the PostController with the PostRepository. So if we would like to test the controller, we have to deal with the complete stack and running an integration test rather than a unit test.
Unit and integration are just two different types of tests with different purposes. Unit tests are built to test a single method in isolation from the rest of the application. This means that the test and the system under test (SUT) must not use external resources (database, file system, networks, etc.) and that the tested code must not depend on other objects. In integration tests, we are writing a test against the complete stack of our application, which means that the class under test is connected to other real classes that can use databases or other external resources.

Unit tests vs. integration tests
What does this mean practically?
Unit tests are usually faster because they are executed completely in memory, while integration tests tend to be slower, since they could depend on external resources. If a unit test breaks, the error is quite certainly in the method that we are testing, but if an integration test breaks, the error could be in other parts of the application, and sometimes it might not be a “real” error (for example, if the connection string is not correct, or if the database server is unreachable).
So are unit tests better than integration tests? Absolutely not—every test is important. Unit tests are designed for micro functionality, while integration tests verify the application in its completeness.
Going back to our PostsController, let’s see how we can refactor it to support better testability. We have already said that the problem is in the constructor that creates an instance of the repository. If we can apply the dependency injection pattern and pass to the controller an abstraction of the repository, we can write unit tests.
So we can refactor the controller to make it receive an instance of the interface IPostRepository:
public class PostsController : ApiController { private readonly IPostRepository _repository; public PostsController(IPostRepository repository) { _repository = repository; }
//... } |
This refactoring allows us to unit test the PostsController because we remove the strong dependency. In our test we can pass a mocked repository:
[Fact] public void GetById_should_return_the_post() { Mock<IPostRepository> repo = new Mock<IPostRepository>(); PostsController controller = new PostsController(repo.Object); controller.Get(42);
repo.Verify(r => r.Get(42)); } |
State testing vs. interaction testing |
We have created a mock instance of the repository and created the controller. We can verify that when we call Get on the controller, the repository is called with the right parameters. Since the repository is a mock object and the controller is not using the real repository, this is a unit test.
But this refactoring breaks our application; even if the test is green, if we run the application and call the Get from the client, we obtain the following error:
Type 'HelloWebApi.Controllers.PostsController' does not have a default constructor" |
What happens here is that since Web API is not capable of building a controller that has external dependencies, it searches for the default constructor. Since it does not find it, it throws an exception.
To resolve this problem we have to implement a custom controller resolver that is able to resolve the dependencies and construct the controller.
Practically, this means we implement the interface IDependencyResolver and change the default resolver to the new one. The interface IDependencyResolver (which implements IDependencyScope and IDisposable) provides two methods:
So for the controllers, the runtime calls GetService. If our custom controller is able to create the required type, it builds it and returns it. If not, it simply returns null so that the runtime continues with the default resolver.
The custom controller can also manage the lifetime of the objects that it creates; the two methods BeginScope and Dispose are used for this. When the framework creates a new instance of a controller, it calls BeginScope, which returns an instance of IDependencyScope. Then, the runtime calls GetService on the IDependencyScope instance to get the controller instance. When the request is completed, the runtime calls Dispose on the child scope, so we can use Dispose to dispose of the controller’s dependencies.
Therefore, in practice, a simple resolver could be something like this:
The most important part is in the GetService method that is in charge of building the requested object based on serviceType. In the previous implementation, we just created the PostsController using the new operator. In a real world scenario, that code should probably be replaced with an inversion of control container that will resolve the object for us.
Now that we have the resolver correctly implemented, we need to add it to the configuration to make sure it works. Add this line to the WebApiConfig.Register method:
Going back to the testing topic, let’s see how we can test the Post action:
public HttpResponseMessage Post(Post post) { _repository.Create(post); var response = Request.CreateResponse(HttpStatusCode.Created);
string uri = Url.Link("DefaultApi", new { id = post.Id }); response.Headers.Location = new URI(uri);
return response; } |
This action creates a new Post object using the controller, and then creates a response with the status code and sets the location header to the newly created post. We would like to test that the response message contains the correct status code and that the header is present.
Testing this code is easy, but requires a bit of setup for preparing the context for the request and response.
The complete code for the test is here:
|
public void Post_Status_is_Created_and_header_contains_the_location() { Mock<IPostRepository> repository = new Mock<IPostRepository>();
var controller = new PostsController(repository.Object); var config = new HttpConfiguration(); var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/"); IHttpRoute route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}"); HttpRouteData routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "posts" } }); controller.ControllerContext = new HttpControllerContext(config, routeData, request); request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config; request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData; controller.Request = request;
HttpResponseMessage response = controller.Post(new Post() { Title = "test", Date = DateTime.Today, Body = "blablabla" }); Assert.Equal(HttpStatusCode.Created, response.StatusCode); Assert.NotNull(response.Headers.Location); } |
The test starts with the creation of the mock repository, which is needed to make the test pass, even if it’s not used in the test. After the creation of the controller, all that code is needed to set up the context in which the Web API needs to run, so it can use the helper methods like Request.CreateResponse and Url.Link. Basically we create the route table, the request object, the configuration, and the ControllerContext.
Note: The sample tests are written using xunit.net (http://xunit.codeplex.com/) as a test.
Next, we call the Post() method by passing a fake Post object. We Assert on the response message to make sure that the status code is correct and that the location header is present.
The POST action is the most difficult action to test. Others like GET and DELETE are much simpler because they don’t have to deal with the request and response mechanism. Therefore, this test is not as beautiful as it should be because the setup code is hard to write and read. We could certainly refactor it, creating helper methods or abstract classes to inherit from, but we can also use a special hosting technique to test the controller in integration with the full ASP.NET Web API stack.
We already talked about in-memory hosting in the previous chapter, and we have already shown how it works. Here we will see how we can use it to test our controllers. With in-memory hosting, we could write integration tests as if the ASP.NET Web API is in a production environment. So a test in this scenario passes the full stack, from the request down to the model binders, controllers, repositories, and back to the response. This is the perfect technique for writing acceptance tests on the behavior of the API.
[Fact] public void Get_with_in_memory_hosting() { HttpConfiguration config = new HttpConfiguration(); WebApiConfig.Register(config); HttpServer server = new HttpServer(config);
HttpClient client = new HttpClient(server); var response = client.GetAsync("http://localhost/api/posts").Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); } |
This test is simpler than the previous one; it creates an in-memory server that “listens” to the requests that are issued using the HttpClient class that it asserts against the response. This is an integration test since it uses the entire application stack. As you can see at the beginning of the test, we have to configure the API using the WebApiConfig class so that the test configuration is the same as it would be in production. The thing to notice is that even if we are testing the PostsController in the test, it is not directly used. Instead, we build a request to the /api/posts URI, the routing system that routes the request to the PostsController (exactly how it happens in a production scenario).
The test for the Post() action is something like this:
[Fact] public void Get_with_in_memory_hosting() { HttpConfiguration config = new HttpConfiguration(); WebApiConfig.Register(config); HttpServer server = new HttpServer(config);
HttpClient client = new HttpClient(server); HashSet<KeyValuePair<string, string>> values = new HashSet<KeyValuePair<string, string>>{ new KeyValuePair<string, string>("Title", "test"), new KeyValuePair<string, string>("Date", "2010-04-11"), new KeyValuePair<string, string>("Body", "blablabla") }; HttpResponseMessage response = client.PostAsync("http://localhost/api/posts", new FormUrlEncodedContent(values)).Result; Assert.Equal(HttpStatusCode.Created, response.StatusCode); Assert.NotNull(response.Headers.Location); } |
We do not need to set up the entire context in this case since it is automatically configured by the in-memory server. What we have to do is build the request. Since this is a POST, the request must have a body. We are using a FormUrlEncodedContent, which is a HashSet with the key-value pairs that represent the information about the Post to be created.
Testing is often considered an accessory to the real application, perhaps because in the past, testing web applications was very difficult. ASP.NET Web API makes testing as easy as writing other parts of the application. And since test-driven development is applicable, our API grows test after test.
In this chapter, we saw how to test Web API, and how to apply inversion of control to inject dependencies so that the tests created are real unit tests.