Testing OWIN applications with HttpClient and OwinHttpMessageHandler

Let's take the simplest, littlest, lowest level OWIN app:

Func<IDictionary<string, object>, Task> appFunc = env =>
{
    env["owin.ResponseStatusCode"] = 200;
    env["owin.ResponseReasonPhrase"] = "OK";
    return Task.FromResult(0);
}

(In reality you'll probably use Microsoft.Owin or LibOwin to get nice typed wrapper around the OWIN environment dictionary instead of using keys like this.)

How do you test this?

Well you could create your own environment dictionary, invoke appFunc directly and the assert the dictionary.

[Fact]
public async Task Should_get_OK()
{
    var env = new Dictionary<string, object>()

    await appFunc(env);

    env["owin.ResponseStatusCode"].Should.Be(200);
    env["owin.ResponseReasonPhrase"].Should.Be(OK);
}

While this would work it, it's not particularly pretty and it'll get messy fairly quickly as you may need to assert cookies, dealing with chunked responses, doing multiple requests (e.g. a login post-redirect-get), etc.

Wouldn't it be nicer to use HttpClient and leverage it's richer API? It would also better represent an actual real-world client. Something like this is desirable:

[Fact]
public async Task Should_get_OK()
{
    using(var client = new HttpClient())
    {
        var response = await client.GetAsync("http://example.com");

        response.Status.Should().Be(HttpStatusCode.OK)
    }
}

There are three ways you can leverage HttpClient in this way.

  1. Start an actual web server using Microsoft.Owin.Hosting.HttpListener, nowin or similar. The issue with this is that because we are using a system resource (a port number) we will very likely get conflicts when running tests in parallel, on CI servers, ncrunch, etc.
  2. Use Microsoft.Owin.Testing.TestServer; which I'm not fond of because the strange HttpClient property that is actually a factory, the lack of cookie and redirect support and the reliance on IAppBuilder.
  3. Use OwinHttpMessageHandler!

OwinHttpMessageHandler

This is an HttpMessageHandler that takes an AppFunc or a MidFunc in its constructor. When passed into an HttpClient it allows invoking of the AppFunc/MidFunc directly, in memory and without the need for a listener (+ port). This makes it very compatible with CI servers, parallel testing etc. It is also faster and cleaner from a setup and tear down perspective.

For example:

[Fact]
public async Task Should_get_OK()
{
    Func<IDictionary<string, object>, Task> appFunc = env => { ... }
    var handler = new OwinHttpMessageHandler(appFunc); // Or pass in a MidFunc
    using(var client = new HttpClient(handler))
    {
        var response = await client.GetAsync("http://example.com");

        response.Status.Should().Be(HttpStatusCode.OK)
    }
}

Once you have that HttpResponseMessage, you can do all sorts of rich assertions. For example, using CsQuery or AngleSharp to assert on the returned HTML.

Options

There are options that allow you to customize the handler:

var handler = new OwinHttpMessageHandler(appFunc)
{
    // Set this to true (default is false) if you want to automatically handle 301
    // and 302 redirects
    AllowAutoRedirect = true,
    AutoRedirectLimit = 10,

    // Set this if you want to share a `CookieContainer`
    // across multiple HttpClient instances.
    CookieContainer = _cookieContainer,
    UseCookies = true,
};

Building an AppFunc from a Startup class

If you are using Microsoft.Owin to construct your pipelines you are probably following the Startup class convention:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseStuff();
        // ...
    }
}

Not many know this but you can simply create an AppFunc by leveraging Microsoft.Owin.Builder.AppBuilder:

[Fact]
public async Task Should_get_OK()
{
    var app = new AppBuilder();
    new Startup().Configuration(app);
    var appFunc = app.Build();
    var handler = new OwinHttpMessageHandler(appFunc);

    using(var client = new HttpClient(handler))
    {
        var response = await client.GetAsync("http://example.com");

        response.Status.Should().Be(HttpStatusCode.OK)
    }
}

Real world examples and other usages

When I am building an application (typically CQRS based) that is HTTP-API-first, if I want to invoke a command, I'll often use HttpClient with OwinHttpMessageHandler in-proc to invoke the API "embedded".

Acknowledgements

While I don't use Microsoft.Owin.Testing, they did have a better implementation of a fake in-memory network stream which I pinched. <3 OSS :)

comments powered by Disqus