Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking HttpClient GetAsync by using Moq library in Xunit test

I am writing a simple unit test for this small service that simply calls external APIs:

public class ApiCaller : IApiCaller
{
    private readonly IHttpClientFactory _httpFactory;

    public ApiCaller(IHttpClientFactory httpFactory)
    {
        _httpFactory = httpFactory;
    }

    public async Task<T> GetResponseAsync<T>(Uri url)
    {
        using (HttpClient client = _httpFactory.CreateClient())
        {
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.Timeout = TimeSpan.FromSeconds(20);
            using (HttpResponseMessage response = await client.GetAsync(url))
            {
                response.EnsureSuccessStatusCode();
                string responseBody = await response.Content.ReadAsStringAsync();

                return JsonConvert.DeserializeObject<T>(responseBody);
            }

        }
    }
}

My first question is: it doesn't seem to be very common practice mocking and therefore testing such services and I am wondering if there is some specific explanation.

Second, I tried to write a simple unit test but I cannot Mock the GetAsync call since HttpClient doesn't implement any interface.

public class ApiCallerTest
{
    private readonly ApiCaller _target;
    private readonly Mock<IHttpClientFactory> _httpClientFactory;


    public ApiCallerTest()
    {
        _httpClientFactory = new Mock<IHttpClientFactory>();
        _target = new ApiCaller(_httpClientFactory.Object);
    }

    [Fact]
    public void WhenACorrectUrlIsProvided_ServiceShouldReturn()
    {


        var client = new HttpClient();
        _httpClientFactory.Setup(x => x.CreateClient(It.IsAny<string>())).Returns(client);

        var httpMessageHandler = new Mock<HttpMessageHandler>();

    }

}
like image 732
Tarta Avatar asked May 09 '19 16:05

Tarta


People also ask

Can we mock HttpClient?

Mocking HttpClient is possible although an arduous task. Luckily there is still a great way to unit test the code. The solution is to mock HttpMessageHandler and pass this object to the HttpClient constructor. When we use the message handler to make HTTP requests, we achieve our unit testing goals.

How do you mock HttpRequestMessage?

Returns("my_ticket"); Mock<HttpRequestMessage> req = new Mock<HttpRequestMessage>(); req. Setup(m => m. CreateResponse( HttpStatusCode. OK, "value")) .

What can be mocked with MOQ?

You can use Moq to create mock objects that simulate or mimic a real object. Moq can be used to mock both classes and interfaces. However, there are a few limitations you should be aware of. The classes to be mocked can't be static or sealed, and the method being mocked should be marked as virtual.

What is MOQ and xUnit?

Moq and xUnit belong to "Testing Frameworks" category of the tech stack. xUnit is an open source tool with 2.62K GitHub stars and 610 GitHub forks. Here's a link to xUnit's open source repository on GitHub.


Video Answer


1 Answers

The code below is what you should use regardless of the method in the HttpClient class you use (GetAsync, PostAsync, etc.). All these methods are created for the convenience of the programmer. What they do is use the SendAsync method of the HttpMessageHandler class.

var mockHttpMessageHandler = new Mock<HttpMessageHandler>();

// Setup Protected method on HttpMessageHandler mock.
mockHttpMessageHandler.Protected()
    .Setup<Task<HttpResponseMessage>>(
        "SendAsync",
        ItExpr.IsAny<HttpRequestMessage>(),
        ItExpr.IsAny<CancellationToken>()
    )
    .ReturnsAsync((HttpRequestMessage request, CancellationToken token) =>
    {
        HttpResponseMessage response = new HttpResponseMessage();

        // configure your response here

        return response;
    });

And then you use it this way:

var httpClient = new HttpClient(mockHttpMessageHandler.Object);
var result = await httpClient.GetAsync(url, cancellationToken);

You can also take a look here How to create mock for httpclient getasync method?

like image 108
Petru Ritivoiu Avatar answered Sep 17 '22 21:09

Petru Ritivoiu