This post continues from earlier article on Unit testing and code coverage for ASP.NET Web API (1/2).
Much about the topic is inspired from the truly magnificent book “The Clean Coder: A Code of Conduct for Professional Programmers” of Robert C. Martin Series, which of course, I definitely recommend.
Specifying the low level architecture
Professional software developers always test their code. It is part of our daily job, we should be proud and flexible on writing tests. It is a proof that our code actually follows our intent, at least on system’s low level. There are many more tests to be followed, composing a testing strategy, but this post is going to focus solely on one aspect of such strategy, the unit tests.
Code outlined in this article can be found here.
The three laws of TDD
Robert C. Martin, aka Uncle Bob, has defined some three simple rules that describe Test Driven Development.
- You are not allowed to write any production code unless it is to make a failing unit test pass.
- You are not allowed to write any more of a unit test that is sufficient to fail; and compilation failures are failures.
- You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
Rule #1 says that you have to write your test first, rather than jumping directly to your business code.
Rule #2 is more peculiar as it states that you should not write more of a unit test if that unit test fails. You need to stop, switch to production code and make it pass.
Rule #3 states that you should only write enough production code to make the test compile or pass. No more.
These three laws lock you into a cycle that is, perhaps, thirty seconds long. You switch from unit test to production code and vice versa. Adding a bit to the test code, adding a bit to the production code. This cycle, for some people may seem slow. Some, have the feeling that they might write more is they don’t write tests at all. But this is an illusion, we tend to write bad code without tests and we spend a lot of time in manual tests. This is the recipe for disaster. We (and probably other people) are not happy with ourselves, in the end.
Search for TDD kata’s over the internet, see other people’s views and comparisons on working on specific problems with or without TDD. Then try to work on kata’s by yourself, using each approach. Try to ride the beast and you will be amazed by the end result.
In order to create some unit tests you need to download the following nuget packages:
- NUnit3. A unit testing framework. Provides all tools needed to write tests, set them up, tear them down, etc.
- Moq. A mocking framework. Provides necessary API to mock interface or abstract dependencies into modules.
A unit test can be a class decorated by the
TestFixtureAttribute. Your test methods must be decorated by the
TestAttribute in order to be identified as tests. If you need to setup some code before running a test, you should create a method with is decorated by the
SetUpAttribute and there initialize all your code needed for the underlying tests.
Test code above follows naming standards. It also follows the AAA (Arrange, Act, Assert) pattern. It is a pattern for writing clean tests, with parts of code related to each other grouped together. What is that it makes a test clean though? Readability is the answer. We want the tests to be readable because their intent is not only to test code but to document it as well, which means other individuals will have to go through them.
Initialization code is grouped together in an imaginary Arrange group. Same for Act and Assert group. Those groups are labeled by one line comments. If you want, you can omit those comments, and separate the groups with a newline. Point is to separate conceptual affinity groups in order to maximize test’s readability.
I have written some tests for this application, some of them will be shown here, some won’t make it for brevity’s sake. I would suggest to go to the repository and check yourselves to get the whole picture.
There are tests for People.Domain project, which consist of ApplicationDbContextTests and RepositoryTests.
There are tests for People.Services project, which consist of PersonServiceTests.
There are tests for People.SelfHostedApi project, which consist of tests for controllers, action selection, routing, custom authorization server.
Let’s explore some various code samples from them.
In People.SelfHostedApi.Tests project, under the Controllers directory you can find tests for Web API controllers.
Let’s see PersonController unit tests. This controller receives an IPersonService type, through constructor injection. So, in tests I need to make sure I pass a mocked object of this type, controlling calls to this object.
This creates a mock of the IPersonService type. The
_personService field, will be fed into PeopleController’s constructor. See the snippet bellow:
_personService mock object exposes the mocked object instance by accessing the its
Object property. Before I inject it into the controller, I fist made sure to mock the
GetPeople() method of the service, as this is the one I care about in this scope. This method will return a mocked result, a list of Person objects.
In act section, I call the actual method I want to test.
In assert section, I just make sure that code does what I expect it to do.
In the act section, from previous example, the Get action returns an
IHttpActionResult. In order to get the command along with the contents it returns we need to use the correct type from
System.Web.Http.Results namespace. Web API team made this feature available in Web API 2, in order to make unit testing easy.
For OK results with content, we expect
T represents the type of content that is returned. Beware through, if you put the wrong
T type in test, you are going to get a null value. For example, if you return a collection, don’t put
OkNegotiatedContentResult<IEnumerable<Person>> as this going to be null. If you return a
List<T> as a content, put a
List<T> in the type. Same goes for other types like
Now, for OK results without content (empty) use the
OkResult type to cast. Same goes for other types that return empty content, like
This is a test to verify that a NotFoundResult is returned to the client, when person with specific id is not found:
In case of
BadRequestResult you cast it to this type when you return an empty BadRequest without content, like the following
In case you return a BadRequest with content, you cast a
And the corresponding test case:
Testing actions which do not depend on controller context
All tests demonstrated above, test controller actions which do not depend on controller context at all. This means, there is no need to create an
HttpRequestMessage or anything like that, just call the controller’s action as normal through code and test the behavior/result returned.
Testing actions which depend on controller context
These tests are similar to previous ones, but with one difference. You are responsible to setup a context for the controller under test. You actually need that in order to get the test passing, or else you are going to receive some
So, let’s see this method from PersonController, which creates a new Person, returns a 202 Created response code, along with the resource URI.
Have you noticed the problem yet? The Created method receives a URI in string and a Person object. The problem is that the creation of the URI depends on the
Request object of the controller, which happens to be of
HttpRequestMessage type. So, it’s required to create a new instance of the PersonController and pass an
HttpRequestMessage instance to its
The following test does exactly this:
In the assertion conceptual affinity block, except of asserting the result content, there is assertion on the current location the new resource is created at. The location consists of the absolute URI of the request plus the Id of the Person object created.
Routing in ASP.NET Web API is based on the
IHttpRoute interface. If you open the source code of the
IHttpRoute interface, you will see various properties and methods, but the main point of interest here is the
GetRouteData method. By this, it is easy to break down the URL to its components and parameters, which is exactly what the following test is doing. Currently, there is only one route configured, which is the Default route, a pretty standard one to match a controller by its name, with an optional Id parameter.
As a side note, the following test is a data driven test, meaning test can be divided to more than one test cases, whereas each runs individually, passing input to the actual test. This can be achieved using the
TestCaseAttribute of NUnit.
Lots of cases here. Each of the parameters on the
TestCaseAttribute are passed as actual parameters to the Test. You need to create an
HttpConfiguration object, which registers the routes (in this case the default route), as well as to create an
HttpRequestMessage with the appropriate method and URL.
In HttpConfiguration initialization, make sure to call the
EnsureInitialized method on the configuration object, after registering the required routes. These actions take place in two following public utility methods. Reason? Readability, as the test above seems much cleaner without having all these methods internals. All you need is some expressive names.
Testing controller and action selection
Last bit to cover in this rather lengthy blog, is about controller and action selection in Web API. You need to make sure that the correct controller and correct action is selected on each request. You can test the routes, but you won’t be sure which controller and action is selected. Methods in Web API, when the design is Restful, are selected based on the request URI and HTTP method, which differentiates from classic RPC calls to specific methods.
There is a way to test this functionality as well, due to
IHttpActionSelector interfaces, which are used by the framework to find the appropriate controller and action.
There are two ways to write those tests, one is to rely on the routing result to be correct, hence takes a form of integration test, while the other is to provide your own route data to decouple the test from routing. First option is much simpler, with little setup, and is the one chosen to test this behavior.
Look at the following example:
Look at the URI’s tested. You see that some URI’s are similar for different actions, with only difference being the HTTP method. This test makes sure that the appropriate action is picked up on a request.
Also, notice how IHttpActionSelector and IHttpControllerSelector instances are fetched, through the
HttpConfiguration.Services object, which is an IoC container, implementing the Service Locator pattern, of type
ServicesContainer. This class is designed to resolve dependencies of the framework itself, so it’s easy for us to fetch the action and controller selector instances.
ControllerDescriptor is fetched from
SelectController method, of course by relying on the request. In order to get the
HttpControllerContext is required (see the utility methods below).
Finally, the assertion is performed on the controller type and the action name to ensure the correct controller and action of that controller are selected.
The utility methods for the test aren’t something special, one creates a new
HttpControllerContext, while the other an
HttpConfiguration as shown earlier.
In order to get some reports on code coverage you need to use the following nuget packages:
- OpenCover (currently using the 4.6.519 version)
- ReportGenerator (currently using the 2.4.5 version)
- NUnitConsoleRunner (currently using 3.4.1 version)
Now, we want to have report for each test project in the solution, while the solution holds 3 different test projects.
Steps are the following:
- Create a .bat file in your project root.
- On the file go to properties (Alt+Enter) and select Copy if newer for the Copy to Output directory option.
- Paste the following code
Code above does three things.
- Uses OpenCover tool, in conjuction with NUnit3 to create a coverage result in an XML format.
- Uses the ReportGenerator tool, to get the coverage result, which is an XML file and convert human readable, pleasing HTML reports.
- Launches browser to show the report.
Take extra care on the OpenCover, NunitConsoleRunner and ReportGenerator version numbers. Be sure to use the correct ones. Go to your packages folder in root and double check.
Replace the NAME_OF_TEST_PROJECT with your test project name (my test project for example is People.Services.Tests) and on filter, replace the NAME_OF_PROJECT with the project name that you are currently testing (I am testing the People.Services project in People.Services.Tests).
Rest of the code is pretty standard, the ReportGenerator is running on the same directory as OpenCover created the _CodeCoverageResult.xml file. It picks it up, creates a new directory _CodeCoverageReport and places all the HTML reports there.
Last three lines of code, are actually starting the default browser, to open the ReportGenerator’s HTML report.
Make sure to run the .bat file from your project’s /bin/CONF/ directory (CONF might be your Debug or Release configuration, whatever you are building against).
A little bit about filters
You need to add filters in your OpenCover configuration, you need to instruct the tool what to look for and what to exclude, or else your reports will be skewed. You use the
This adds a specific namespace to the coverage. It adds all types in People.Services assembly, that start with the People.Services namespace. If you want to exclude something use the – symbol instead.
The following filter includes all types under People.Domain namespace in People.Domain assembly, but excludes every type that is under
Listing 1-1. People.Domain.Tests coverage report. 100%
Listing 1-2. People.Services.Tests coverage report. 100%
Listing 1-3. People.SelfHostedApi.Tests coverage report. 90.5%
Some code slips away from the coverage and is seen as untested, but it’s code that doesn’t have a value to be tested, like the framework’s code. Possible defects from such code can be easily tracked in upper level tests, like acceptance, integration, system or exploratory tests.
In this post, we learnt how to author unit tests for the class libraries, representing different application layers, as well as authoring unit tests for ASP.NET Web API. We also tested Web API specific components, like routes, action selection, introducing NUnit’s data driven tests.
Finally, we learnt how to install and configure OpenCover and ReportGenerator tools, make them work together with the tests and code, in order to provide code coverage reports.
In the next session I am going to talk a little bit more about BDD and integration testing in ASP.NET Web API. I am also going to explore testing in OWIN pipeline.
If you liked this blog post, please like, share and subscribe! For more, follow me on Twitter @giorgosdyrra.