In the past the only way I’ve known how to deal with mocking Http/Web clients is to wrap the methods using a custom interface. This is a lot of overhead and I feel teaches us its ok to write code that was not written in a way that is testable from the start. Wrappers are a smell in my opion.
.Net Core have solved the HttpClient
problem by providing a way to inject a HttpClient
instance using .AddHttpClient
which is in the Microsoft.Extensions.DependencyInjection
namespace. This is great as it then gives the consumer a valid object for them to setup things like their BaseAddress
and other settings needed.
Mock HttpMessageHandler
- In the pipeline add
1 | services |
- In the constructor for
FooServiceClient
you will automagically get aHttpClient
instance which you can then set any propertys needed.
1 | public class FooServiceClient |
- Now if you try mock to perhaps assert what the
HttpRequestMessage
request was, or even thatSendAsync
was called it cannot be done as it has no override modifier. If you try construct the mock it will protest at runtime about setup / verification expressions.
1 | var httpClientMock = new Mock<HttpClient>(); |
Injecting the above with httpClientMock.Object
will be fine but the .Verify
will fail as expected.
1 | Message: |
3.1. The work around is to look at the HttpClient
constructor overloads, one takes an instance of HttpMessageHandler
which has an abstract SendAsync
method that is used by HttpClient under the hood. Credit to Gaute Meek Olsen’s post explaining this far better than I could.
Create the httpMessageHandlerMock
1 | var httpMessageHandlerMock = new Mock<HttpMessageHandler>(); |
You can also add to the response body if needs be, the below is manual json - best to do it with a serialized object of what ever your end point returns.
1 | var response = new HttpResponseMessage { |
Then define a .Setup
to return the response above. As SendAsync
is protected we need to use .Protected()
Note that if in your implementation of HttpClient
calls .GetAsync(...)
this will still work as internally GetAsync
will eventually call SendAsync
.
1 | httpMessageHandlerMock |
Create the client for dependancy injection
1 | var httpClient = new HttpClient(httpMessageHandlerMock.Object); |
A mock .Verify
to check SendAsync
was called once with an expected request.
1 | httpMessageHandlerMock |
Beauti!
References
Delegating Handler
Alternatively we can use a named http client and add a custom handler using the base class DelegatingHandler
Create the handler
1 | public class StubDelegatingHandler : DelegatingHandler |
Inject in tests
1 | private const string NamedHttpClient = "MyClient"; |
Boom!