ASP.NET Web API Integration Testing with One Line of Code

A very popular post about integration testing ASP.NET Web API was published quite some time ago. However, since then, OWIN has been released. OWIN makes integration testing ASP.NET Web API much simpler. This post describes what is required to set an OWIN-based integration testing framework up.

This, believe it or not, only requires a single line of code with OWIN self-hosting! It assumes that your web API project is powered by ASP.NET Web API 2.2 with OWIN.

The basic idea behind this particular approach is that the integration test project self-hosts the web API project. Consequently, the integration tests are executed against the self-hosted web app. In order to achieve this, the web API project must be modified a little bit. Fortunately, this is easy with OWIN.

A web app powered by OWIN has two entry points: Application_Start() and Startup.Configuration(IAppBuilder). There isn’t a big difference between the entry points but it is important to know that Startup.Configuration is invoked a bit later than Application_Start().

So the integration test project can self-host the web API project, all web API initialization logic must be moved to Startup.Configuration(IAppBuilder) as follows:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var configuration = new HttpConfiguration();
        WebApiConfig.Register(configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        // Execute any other ASP.NET Web API-related initialization, i.e. IoC, authentication, logging, mapping, DB, etc.
        ConfigureAuthPipeline(app);
        app.UseWebApi(configuration);
    }
}

Not much is left in the old Application_Start() method:

public class WebApiApplication : HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RouteConfig.RegisterRoutes(RouteTable.Routes);
    }
}

Now, in order to test your web API project, add the following OWIN NuGet packages to your tests project:

PM> Install-Package Microsoft.Owin.Hosting
PM> Install-Package Microsoft.Owin.Host.HttpListener

Following is the single line of code that is required to self-host your web API project:

using (var webApp = WebApp.Start<Startup>("http://*:9443/"))
{
    // Execute test against the web API.
    webApp.Dispose();
}

That single line starts the web API up.

It is better that this is set up once for all integration tests for your tests project using MSTest’s AssemblyInitialize attribute as follows:

[TestClass]
public class WebApiTests
{
    private static IDisposable _webApp;

    [AssemblyInitialize]
    public static void SetUp(TestContext context)
    {
        _webApp = WebApp.Start<Startup>("http://*:9443/");
    }

    [AssemblyCleanup]
    public static void TearDown()
    {
        _webApp.Dispose();
    }
}

Now, the test code for testing a web API, which is self-hosted at localhost:9443 and protected using token-based authentication, can be written as follows:

[TestMethod]
public async Task TestMethod()
{
    using (var httpClient = new HttpClient())
    {
        var accessToken = GetAccessToken();
        httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", accessToken);
        var requestUri = new Uri("http://localhost:9443/api/values");
        await httpClient.GetStringAsync(requestUri);
    }
}

The above approach has a wide range of advantages:

  • You don’t have to deploy the web API project
  • The test and web API code run in the same process
  • Breakpoints can be added to the web API code.
  • The entire ASP.NET pipeline can be tested with routing, filters, configuration, etc.
  • Any web API dependencies that will otherwise be injected from a DI container, can be faked from the test code
  • Tests are quick to run!

On the other hand, there are a few limitations, since there are different behaviours between IIS hosting and self hosting. For example, HttpContext.Current is null, when the web API is self-hosted. Any asynchronous code might also result in deadlocks when it runs in IIS while it runs fine in the self-hosted web app. There are also complexities with setting the SSL certificate for the self-hosted web app. Please note that Visual Studio should run with elevated elevated privileges.

Nevertheless, with a careful consideration, integration tests can be authored to run against a self-hosted or cloud-hosted web API.

This provides a tremendous productivity boost, given the easy ability to run integration tests in process, with a setup cost that is negligible as described in this post.

Update:

It was suggested in the comments to utilize In-Memory hosting instead of Self-Hosting solution. (See details here)

Another nuget package should be referenced: Microsoft.Owin.Testing. The resulting in-memory api call is executed as follows:

using (var server = TestServer.Create<Startup>())
{
    // Execute test against the web API.
    var result = await server.HttpClient.GetAsync("/api/values/");
}

Unfortunately, in-memory solution doesn’t work for me out of the box. Seems like it can’t handle authentication. I use UseJwtBearerAuthentication (JWT bearer token middleware) and my api calls result in 401.

Azure Table Storage little gem – InsertOrMerge

This blog describes the usage of the InsertOrMerge operation for Azure Table Storage.

Each entity in Table Storage is defined by the PartitionKey/RowKey combination. InsertOrMerge will insert the entity if it doesn’t exist and, if it exists, it would merge the properties of updated entity with the existing one. For more details, see the Azure Storage blog.

When comparing with the existing table schema, not all properties are required to be specified for this operation. InsertOrMerge only merges the specified properties; the rest of them are ignored. As a result, this feature is very convenient — for instance, when aggregating dashboard data. There might be a background process, which simultaneously assembles various pieces of dashboard data from multiple sources. There is an obvious race condition, which InsertOrMerge solves straight forward.

To demonstrate this, let’s define the following entity that we would like to store in Table Storage.

public class Dashboard : TableEntity
{
    public string AppleType { get; set; }
    public int ApplesCount { get; set; }
    public string OrangeType { get; set; }
    public int OrangesCount { get; set; }
}

Now, let’s define two other entities that contain only some of the existing properties of the Dashboard entity:

public class DashboardApple : TableEntity
{
    public string AppleType { get; set; }
    public int ApplesCount { get; set; }
}

public class DashboardOrange: TableEntity
{
    public string OrangeType { get; set; }
    public int OrangesCount { get; set; }
}

The following code saves the two entities defined above using the InsertOrMerge operation and then retrieves the full entity.

public async Task<Dashboard> Save(
        DashboardApple dashboardApple,
        DashboardOrange dashboardOrange)
{
    var storageAccount = ...
    var tableClient = storageAccount.CreateCloudTableClient();
    table = tableClient.GetTableReference("Dashboard");
    table.CreateIfNotExists();

    var t1 = table.ExecuteAsync(TableOperation.InsertOrMerge(dashboardApple));
    var t2 = table.ExecuteAsync(TableOperation.InsertOrMerge(dashboardOrange));

    await Task.WhenAll(t1, t2);

    var tableResult = await table.ExecuteAsync(
            TableOperation.Retrieve<Dashboard>(partitionKey, rowKey));

    return (Dashboard)tableResult.Result;
}

To test the above code the two entities with an identical PartitionKey/RowKey must be created:

var dashboardApple = new DashboardApple
{
    PartitionKey = "myPartition",
    RowKey = "myRow",
    AppleType = "GoldenDelicious",
    ApplesCount = 5
};

var dashboardOrange = new DashboardOrange
{
    PartitionKey = "myPartition",
    RowKey = "myRow",
    OrangeType = "Naval",
    OrangesCount = 10
};

var dashboard = await Save(dashboardApple, dashboardOrange);

This results in a single entity in the table with the following properties:

{
    PartitionKey = "myPartition",
    RowKey = "myRow",
    AppleType = "GoldenDelicious",
    ApplesCount = 5,
    OrangeType = "Naval",
    OrangesCount = 10
};

The data has been aggregated.

Evolution of coding

Many years ago, back in Uni, I saw 2 guys in a computer lab writing a whole programming assignment without running it even once. The program was of relatively decent size written in C and consequently there were hundreds of compilation errors. That’s so silly, I thought…

After graduation I used to be a C++ programmer. The syntax sometimes was quite tricky and you would often compile after every new line of code. Sometimes, you would dare to write a whole function, just to find 10 compilation errors.

Since then the way I code has changed with help of modern IDE, Visual Studio as well as ReSharper – a productivity plugin. I never get to use just a simple text editor to write a program anymore. Real time static code analysis allows writing code while checking for compilation errors in the current file. With abundance of automatic refactorings a developer can efficiently manipulate a large amount of code.  However, there might be a compilation error in another part of the solution, you still need to run unit tests, debug etc.

Now, for quite some time I’ve been using a new tool called NCrunch – a real time code coverage tool. The idea is simple: it builds Visual Studio solution and runs all available unit and integration tests automatically in the background while you are still typing. There is no need even to save a file. Initially, I thought to give it a go, as it was an innovative way to quickly see the code coverage. However, it turned out to be much more than that…

As my code has a fair amount of automated unit and integration test coverage, the entire system becomes “alive”. Combination of Visual Studio, ReSharper and NCrunch creates quite an extraordinary experience. Not only do I not build my solution too often these days, I only occasionally need to debug my code.

While typing you are being notified of any compilation error, any broken logic, any not registered component in DI container, any db error, etc. (assuming there is automated test to cover those scenarios)

The new experience reminds me of those guys from the uni lab, who never got to run their program before finishing the entire assignment. Not only technologies and development practices are evolving, but the entire coding experience is evolving too. In the past I have followed the pattern: code – build – code – build – debug – code…. Now my development pattern is: code – code – code – code – deploy. (I still need to debug occasionally)

There are additional, interesting projects promising to further revolutionize the ways we interact with computers through programming languages. One such idea is real time state output of a method while it is being written. Here is amazing video by Bret Victor – Inventing on Principle: http://vimeo.com/36579366. There was one attempt to achieve the same in c# code. http://ermau.com/making-instant-csharp-viable-visualization/ https://github.com/ermau/Instant It looks very promising for algorithms development, but unfortunately, there are no solid commercial products available yet.

Below are some snapshots of nCrunch. For more details go to www.ncrunch.net:

1) By clicking on a dot you can see a list of all tests covering that line, passing and failing.

Image

2) By clicking on a “x” you can see where the code failed

Image

3). The test’s assertion failure is clearly seen below:

Image

4) Compilation errors are indicated in a following windows:

ncrunch_comp_errors

NCrunch is being actively developed and maintained. Give it a try!