Reputation: 14185
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?
Upvotes: 3
Views: 3028
Reputation: 5794
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.
Upvotes: 1
Reputation: 731
If you really need to mock the HttpClient itself, take a look at this lib: https://github.com/richardszalay/mockhttp
From the docs:
var mockHttp = new MockHttpMessageHandler();
// Setup a respond for the user api (including a wildcard in the URL)
mockHttp.When("http://localhost/api/user/*")
.Respond("application/json", "{'name' : 'Test McGee'}"); // Respond with JSON
// Inject the handler or client into your application code
var client = mockHttp.ToHttpClient();
var response = await client.GetAsync("http://localhost/api/user/1234");
// or without async: var response = client.GetAsync("http://localhost/api/user/1234").Result;
var json = await response.Content.ReadAsStringAsync();
// No network connection required
Console.Write(json); // {'name' : 'Test McGee'}
Upvotes: 0