Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mock HttpClient SendAsync method

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?

like image 790
user1765862 Avatar asked Jul 07 '26 07:07

user1765862


1 Answers

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.

like image 119
Matt Thomas Avatar answered Jul 09 '26 22:07

Matt Thomas



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!