Microsoft’s ASP.Net Web API 2.2 allows you to easily create REST style APIs on an IIS website. Microsoft has some great documentation on how to get started with it, so I won’t rehash that here. Instead, I’m going to go a little deeper into some powerful features that can be used with Web API.
- Part 1 - Customizing auto-generated documentation
- Part 2 - HTTP Response Codes
- Part 3 - HTTP Error Codes from Exceptions
- Part 4 - OData URL Query Options
- Part 5 - DTO Transformations and Automapper
- Part 6 - Testing with EF Rollbacks across HTTP (this article)
A few weeks ago I wrote an article called Cleaning Up EF 6 Tests With Transactions Rollbacks, where I showed how to create integration tests that set up some data in a database, run a test against the data, and then roll back all changes to the data. The rollback was possible because all of the changes to the data were wrapped up inside a transaction.
This posts extends that idea, but instead of a test calling methods on a repository or service layer, this test makes an HTTP call against a Web API endpoint, while preserving the ability to revert all changes to the database as part of a transaction rollback.
The interesting part here is that we will create a database context, start a transaction against that context, create some test data, and then spin up a Web API server that uses that same context. When we’re done with our tests, we’ll roll back the transaction so that the database changes are all reverted.
2016-01-27 Update - clarified when Configuration is available in an API Controller
First, let’s look at the
TransactionTest class that we created earlier. If you haven’t seen this before, I’d recommend reviewing my earlier post.
public class TransactionTest
We’ve seen that before. It allows us to create a test inside a database transaction that can be rolled back. How do we extend this to Web API calls? Here is the code:
Let’s look at this one piece at a time. First, we have the
WebApiHostedTestBaseStart. Because of the
[TestStart] attribute, this will be run before each test is run. Because this class inherits from the
TransactionTest class, the
TransactionTestStart method has already run, so we have a context ready. The
WebApiHostedTestBaseStart does the following:
- It creates an
HttpConfigurationand adds the
contextas a property on this configuration. (the
Propertiesproperty is a collection of arbitrary objects that we can see inside our Web API code. This allows us to pass the context into our Web Api project. We’ll look at that momentarily)
- It creates an in-memory
HttpServer, which allows us to run our Web API project in the test runner process without having to spin up IIS or any other web server.
- It creates an
HttpClientand configures it to connect to our in-memory
WebApiHostedTestBaseEnd method runs after each test is run because of the
[TestCleanup] attribute. This cleans up the objects that we created in
Get method, given a URL, makes a HTTP GET call to the server, receives JSON back from the server, and deserializes the JSON into a strongly typed object of type
T. The deserialization happens in this line:
var data = (T)((ObjectContent)(response.Content)).Value;
Get method then returns a TestHttpResponse, which is an object that contains the strongly typed
T along with the raw HTTP response. This allows our test to check the data that was returned, and it allows our test to check the HTTP response codes and headers that are returned. A Web API is composed of many HTTP endpoints that should return various HTTP responses codes, and this allows us to test those response codes.
Here is the implementation of
public class TestHttpResponse<T>
Next, we’ll make use of the context that this sets up for us. In our API controller, we’ll need to grab the context from our
HttpConfiguration is a property on all controllers that inherit from
ApiController. Our code above created and then injected the context into the configuration with this line:
config.Properties["context"] = context;
And this is how we can make use of that context in our API Controllers:
private FooContext _context;
2016-01-27 Update - The Configuration property cannot be used in the constructor for your API Controller, because it is not populated until after the controller is constructed. If you try to use Configuration in the constructor, it will be null. Instead, it can be accessed on-the-fly by creating a property, such as seen above, that will only be used after the object is created.
Now that we have all of these great things set up, how do we use them? We need to create a test class that inherits from
The goal of the code in this post is to make these tests simple and readable. The test adds data to your context, makes an HTTP call, specifying what type of data it expects to get back (
Item in this case), and it then verifies that the HTTP Response looks good and that the data that is returned looks good. The test itself has minimal boilerplate code, and it cleans up after itself.
Thanks for sticking with me in this series on Web API. Feel free to tweet me @codethug if you’d like to follow up on anything.