My code is using HttpClient in order to retrieve some data
HttpClient client = new HttpClient
{
BaseAddress = new Uri("myurl.com"),
};
var msg = new HttpRequestMessage(HttpMethod.Get, "myendpoint");
var res = await client.SendAsync(msg);
how can I mock this SendAsync method on HttpClient and inject it inside .net core ServiceCollection?
I tried to mock like this
var mockFactory = new Mock<IHttpClientFactory>();
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("{'name':thecodebuzz,'city':'USA'}"),
});
var client = new HttpClient(mockHttpMessageHandler.Object);
mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(client);
but how to inject this mockFactory into ServiceCollection? Or maybe there is some easier or different way around?
Instead of mocking the HTTP call, why not encapsulate it? Then you can mock the encapsulation/abstraction.
For example:
interface IClient
{
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default);
}
class HttpClientAdapter : IClient
{
readonly HttpClient _client;
public HttpClientAdapter(HttpClient client)
{
_client = client;
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) => _client.SendAsync(request, cancellationToken);
}
Make your code depend on the IClient interface. During normal usage you would use a real HttpClient in the HttpClientAdapter implementation. And for tests you could mock the IClient.
Note it might be more useful to you to make the abstraction a little higher level than this. For example, if you're expecting to parse a JSON string from HTTP responses into some DataObject then maybe your IClient interface should look more like this:
class DataObject
{
public string Name { get; set; }
public string City { get; set; }
}
interface IClient
{
Task<DataObject> GetAsync(CancellationToken cancellationToken = default);
}
public class ClientImplementation : IClient
{
readonly HttpClient _client;
public ClientImplementation(HttpClient client)
{
_client = client;
}
public async Task<DataObject> GetAsync(CancellationToken cancellationToken = default)
{
var response = await _client.SendAsync(...);
var dataObject = new DataObject();
// parse the response into the data object
return dataObject;
}
}
The advantage of drawing the line here is your tests will have less work to do. Your mock code won't have to set up HttpResponseMessage objects, for example.
Where you choose to draw the boundaries for your abstractions is totally up to you. But the key takeaway is: once your code depends on a small interface then it's easy to mock that interface and test your code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With