Unit Testing a Method That Uses HttpClient

Full source code available here.

In this post I’m going to show you how test an action method of controller that uses a HttpClient. When performing this test you want to isolate just the code of the action method for testing, you want to remove the dependency on the HttpClient. I hoped it would be simple, that there would be an IHttpClient, but there is not.

Instead I mocked the HttpMessageHandler, then pass it to the constructor of the HttpClient. The HttpMessageHandler is set to return a list of numbers in response to any request.

The constructor of the controller takes the HttpClient as a parameter, usually passed by dependency injection.

public class ValuesController : Controller
    readonly IAsyncPolicy<HttpResponseMessage> _httpRetryPolicy;
    private readonly HttpClient _httpClient;

    public ValuesController(HttpClient httpClient)
        _httpClient = httpClient;

In the test class I mock the HttpMessageHandler, set its SendAsync method to return OK and a list of numbers in response to any request. I then pass the mocked HttpMessageHandler to the constructor of the HttpClient.

Now the HttpClient will return the list of numbers I expect and I my test just tests the code of the action method.

The rest of the test is written as normal.

public async Task GetTest()
    string numberJson= JsonConvert.SerializeObject(new List<int>() { 1, 2, 3, 4, 5 });

    var httpMessageHandler = new Mock<HttpMessageHandler>();
        .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
        .Returns(Task.FromResult(new HttpResponseMessage
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent(numberJson, Encoding.UTF8, "application/json"),

    HttpClient httpClient = new HttpClient(httpMessageHandler.Object);
    httpClient.BaseAddress = new Uri(@"http://localhost:63781/v1/");
    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    ValuesController controller = new ValuesController(httpClient);

    IActionResult result = await controller.Get();

    OkObjectResult resultObject = result as OkObjectResult;

    List<int> numbers = resultObject.Value as List<int>;
    Assert.Equal(5, numbers.Count);

Full source code available here.

Using the Polly Timeout when making a Http Request

Full source code available here.

When making remote service requests the remote side will sometimes take longer than acceptable to respond. You have a few choices in handling this.

1. Put up with it, but this means you’re stuck waiting for some unknown period and hope you get a response.
2. Wait for the local HttpClient to timeout.
3. Fire off more requests expecting one of them to respond quickly. But now you have multiple requests open holding resources on your side.

Polly offers another approach. The Polly Timeout Policy allows you to specify how long a request should take to respond and if it doesn’t respond in the time period you specify, a cancellation token is used to release held resources.
The Timeout policy can be combined with a retry policy to fire off another request as soon as the timeout occurs.

This is great for GET requests, but be careful with anything that has side effects, search for idempotent if you don’t know what I mean.

I define two policies, the first is a simple Retry Policy that is set to retry three times. The other is a Timeout Policy that is set to react if no response is received within one second.

readonly RetryPolicy<HttpResponseMessage> _httpRetryPolicy;
readonly TimeoutPolicy _timeoutPolicy;

public CatalogController()
	_httpRetryPolicy =
		Policy.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)

	_timeoutPolicy = Policy.TimeoutAsync(1);

Next I chain (or wrap) these policies around a http request executed by HttpClient.

HttpResponseMessage response = await 
	_httpRetryPolicy.ExecuteAsync(() =>
		_timeoutPolicy.ExecuteAsync(async token => 
			await httpClient.GetAsync(requestEndpoint, token), CancellationToken.None));

Line 4 makes the request to the remote service using the HttpClient.
Line 3, executes the timeout policy, if the http client does NOT respond with 1 second the timeout policy will throw a TimeoutRejectedExcetion.
Line 2, the retry policy condition will trigger when a TimeoutRejectedException occurs, and a retry will be performed.
Line 1, sets the return value from the _httpRetryPolicy to the HttpResponse.

1. The HttpClient calls the remote inventory service which has been written to take 10 seconds to respond for the first three requests, the fourth replies promptly.
2. For the first three requests, after 1 second no response is received from the remote controller, so the retry policy throws a TimeoutRejectedException and cancels the request by the HttpClient.
3. The retry policy condition is triggered and the a retry is executed.

This sequence repeats itself two more times.

4. Finally, on the fourth attempt the request succeeds in less than a second and the a response is returned.

Timeout lets you preempt a slow response and when combined with a retry policy it allows you to send another request quickly.

Full source code available here.