Effectively stubbing remote HTTP service dependencies with HttpClient Interception
Posted on Thursday, 21 May 2020
Recently I've been reading the new Software Engineering at Google book where I've found the chapters on testing to be particularly interesting. One paragraph that especially resonated with my own personal experiences with testing was the following excerpt:
This chapter got me thinking a lot about my preference for testing units of behaviour as opposed to implementation details, and as someone that works with a lot of services that communicate via HTTP there's one particular library that's helped me time and time again, so much so that I feel deserves its own post.
Introducing HttpClient Interception
From a high level, HttpClient Interception is a .NET Standard library designed to intercept server-side HTTP dependencies.
Prior to learning about it the two patterns I most frequently used in order to stub responses from an upstream API were either to create a custom
HttpMessageHandler (which would return desired responses/assertions on a given input), or create an interface over an
HttpClient. Both approaches tend to be painful (the former being difficult to mock due to lack of an interface and the latter somewhat the same).
Let's take a look
HttpClient Interception offers a convenient builder pattern over returning a desired response for a given input. Let's take a look at what a simple test would look like this:
In this example we create an instance of
HttpRequestInterceptionBuilder then specify the request we wish to match against and the response we'd like to return. We then register the builder against our
HttpClientInterceptionOptions type and use it to create an instance of an
Now any requests that match our builder's parameters will return the response defined (a
500 response code) without the request leaving the
At this point it's worth calling out the
true ensures no request leaves an
HttpClient created or modified by HttpClient Interception. This is helpful for ensuring you have visibility over unmatched requests, otherwise leaving
ThrowOnMissingRegistration as false could mean your request gets handled by the real upstream service, causing a false positive or having a negative impact on your test without you being aware of it.
Though naturally it all depends on your use case so in some instances this may actually be a desired behaviour so consideration is required.
Matching multiple requests
If you want to stub the response to more than one HTTP request you can register multiple builders like so:
Here I've registered two separate endpoints on the same
HttpClientInterceptorOptions object which is then used to create our
HttpClient. Now requests to any of the two registered endpoints will result in either a Not Found response (404) or an Internal Server Error (500) response.
Creating an HttpMessageHandler
Not all libraries will enable you to use a custom HttpClient, some may only offer you the ability to add a new
HttpMessageHandler, thankfully HttpClient Interception can also create an
HttpMessageHandler that can be passed to the
Using HttpClientInterception with ASP.NET Core and IHttpClientFactory.
Now we've seen what HttpClient Interception can offer, let's take a look at how we could use it to test an ASP.NET Core application depends on an upstream API. This example is taken straight from the Sample application within the HttpClient Interception repository.
Imagine we have an application that makes a remote call to GitHub’s API via Refit. This is what the controller action might look like:
To test this we can utilise HttpClient Interception to stub the response from GitHub's API with the following fixture (based on the common WebApplicationFactory approach documented here).
Notice that when overriding
ConfigureWebHost we're taking advantage of the
IHttpMessageHandlerBuilderFilter interface to add an additional
HttpMessageHandler (created by HttpClient Interception) to any
HttpClient created via the IHttpClientFactory API.
It's worth noting that you can also register the
HttpMessageHandler using the
ConfigurePrimaryHttpMessageHandler (see the docs here) method that lives on the
Now we can easily control the responses from the GitHub API from within our tests, without having to mock any internal implementation details of our application.
Hopefully this post has demonstrated just how helpful and versatile HttpClient Interception can be and how it can create maintainable tests that don't break the moment you change an implementation detail. I'd highly recommend you check it our as this post only scratches the surface of what it can do.
I'd like to close this post with a quote from Kent Beck:
"I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence."