Category Archives: Desenvolvimento

Novidades, críticas, comentários sobre o desenvolvimento e programação de sistemas, assunto que tanto agrada este autor.

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

Selenium – How to set up automatic downloads and change the default directory

Yeah, as you might have noticed, I love Selenium and I can’t live without. Every piece of code I write needs to call Selenium… all right, that was me overreacting about it, but, you have got my point.

Download a file using Selenium is such a basic task. However, you don’t have access to where file is going to be downloaded or how to click on the confirmation box to proceed with the download. Well, Chrome allows you to change that using preferences, which in Selenium is called Capabilities or ChromeOptions. Firefox can also do it, but, Chrome has way more options for that.

This can be done using the “SetPreference” method of the WebDriver element, which is required to be done before you start the navigation, that means, before you call the Get “http://your.website.address”.

The code block below allows you to:

  1. Activate the automatic download (without confirmation)
  2. Change the default download directory to same as the workbook
Private Sub AbreEConfiguraOChrome()
    Dim driver As New Selenium.ChromeDriver
    driver.SetPreference "download.default_directory", Replace(ThisWorkbook.FullName, ThisWorkbook.name, "")
    driver.SetPreference "download.directory_upgrade", True
    driver.SetPreference "download.prompt_for_download", False
    driver.Get "http://endereco.do.seu.site"
    'aqui começa seu código
End Sub

Piece of cake, right? Ok, it took me a while to figure this out, but it’s there!

Enjoy it!