Unit Testing a Method That Uses HttpClient

Full source code available here.

In this post I’m going to show you how to 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.

 1public class ValuesController : Controller
 3    readonly IAsyncPolicy<HttpResponseMessage> _httpRetryPolicy;
 4    private readonly HttpClient _httpClient;
 6    public ValuesController(HttpClient httpClient)
 7    {
 8        _httpClient = httpClient;
 9    }
10	//snip..

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.

 2public async Task GetTest()
 4    //Arrange
 5    string numberJson= JsonConvert.SerializeObject(new List<int>() { 1, 2, 3, 4, 5 });
 7    var httpMessageHandler = new Mock<HttpMessageHandler>();
 8    httpMessageHandler.Protected()
 9        .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
10            ItExpr.IsAny<CancellationToken>())
11        .Returns(Task.FromResult(new HttpResponseMessage
12        {
13            StatusCode = HttpStatusCode.OK,
14            Content = new StringContent(numberJson, Encoding.UTF8, "application/json"),
15        }));
17    HttpClient httpClient = new HttpClient(httpMessageHandler.Object);
18    httpClient.BaseAddress = new Uri(@"http://localhost:63781/v1/");
19    httpClient.DefaultRequestHeaders.Accept.Clear();
20    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
22    ValuesController controller = new ValuesController(httpClient);
24    //Act
25    IActionResult result = await controller.Get();
27    //Assert
28    OkObjectResult resultObject = result as OkObjectResult;
29    Assert.NotNull(resultObject);
31    List<int> numbers = resultObject.Value as List<int>;
32    Assert.Equal(5, numbers.Count);

Full source code available here.

comments powered by Disqus