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
FooServiceClientyou will automagically get aHttpClientinstance which you can then set any propertys needed.
1 | public class FooServiceClient |
- Now if you try mock to perhaps assert what the
HttpRequestMessagerequest was, or even thatSendAsyncwas 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!