Tag Archives: Testes Unitários

Mocking the HttpClient on AspNet Core

The issue?

Even if you don’t live in the microservices world, you will eventually be in need to make a REST call to some API out there, especially in the AspNet Core world, and the best way to do it is through the HttpClient class.

This class gives you pretty much everything you need, Get, Post, Put, Patch and Delete methods, which map one-to-one to the respective HTTP verbs. The problem is, if you need to Unit Test your class, you may have problems as the methods mentioned above aren’t virtual and the HttpClient class inherits from another class that is not related to these.

Well, you might have noticed (have you?) that the subtitle has a question mark. There is a reason for it. If you do the right implementation, HttpClient will not be an issue for your Unit Tests.

Everything lies on SendAsync method

If there is something beautiful in the AspNet Core world is that it is open source. YES! Every single method and property you have access to has its code open the be read at anytime on GitHub, and yes, HttpClient is part of it! I will leave the link to its source code at the bottom.

After looking close at the source code (which is well written by the way), you figure that all methods except the GET ones (Get, GetAsync, GetStringAsync, etc) make usage of the SendAsync. The SendAsync receives as a mandatory parameter a HttpRequestMessage, which is basically what you send through an HTTP call (body, headers, etc). There is an overload that gets an additional CancellationToken parameter, and that’s the one we are looking for because it is overridable. If you know what a CancellationToken is, just use it, if you don’t, just use Cancellation.None for now.

The code below is an example of a POST being done using HttpClient in a common WebApi Controller.

public class MyController : ControllerBase
{
	private readonly HttpClient client;

    	public MyController(HttpClient client)
    	{
        		this.client = client;
}

	public async Task SomePost(Criteria criteria)
	{    
	    // I am ignoring headers or anything else you might need to send 
	    // The HttpClient is injected via constructor
		var stringContent = new StringContent(JsonConvert.SerializeObject(criteria), Encoding.UTF8, "application/json");
		var result = await this.client.PostAsync($"url", stringContent, CancellationToken.None);
		return Ok(result);
	}
}

The code is fairly simple and it posts something to an endpoint. Now, the unit test that covers and mock HttpClient PostAsync method:

[Fact]
public async Task ItShouldCallSendAsyncAtLeastOnceWhenPosts()
{
	// Moq and xUnit are being used here
	var httpClient = new Mock();
	httpClient
	    .Setup(x => x.SendAsync(It.IsAny(), CancellationToken.None))
	    .ReturnsAsync(new HttpResponseMessage()
	    {
	        StatusCode = HttpStatusCode.OK,
	        Content = new StringContent("{}"),
	    }).Verifiable();

	var controller = new MyController(
	    httpClient.Object
	);

	var criteria = new Criteria() { };

	var result = await controller.SomePostPost(criteria);

	httpClient.Verify(x => x.SendAsync(It.IsAny(), Cancellation.Token), Times.Exactly(1));
}

From what I can tell, the test above passes! Note that I am not mocking PostAsync, but SendAsync as the first one is not mockable the second one is called by the first. In case you want to verify anything sent to the PostAsync, you can use CallBacks offered by Moq lib.

What about Get/GetString/GetStringAsync?

So, what about the “GETs”? Well, it’s simple, don’t use them! Here is why.

The Gets have the advantage of being simple and straightforward, but they only work well for text results and anything bad that happens will throw an exception. Fortunately, there is an alternative. The SendAsync!

Take a look at the code below and it will become more clear:

// instead of this
public async Task GetContent()
{
	var result = await this.client.GetStringAsync("https://some.api");
	return Ok(result);
}

// do this
public async Task GetContent()
{
	var requestMessage = new HttpRequestMessage(HttpMethod.Get, "https://some.api");
	var response = await this.client.SendAsync(requestMessage);

	if (response.IsSuccessStatusCode)
	{
	    var result = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
	    return Ok(result);
	}
	else
	{
	    return StatusCode((int)response.StatusCode, response.Content.ReadAsStringAsync().Result);
	}
}

By doing this, your return is more consistent and also gives you the chance for a better error check as the response contains everything you need, like StatusCode, etc.

Enjoy it!

HttpClient source code:

https://github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/System/Net/Http/HttpClient.cs

A working example of it:

https://github.com/Tomamais/dotnet_mocking_httpclient