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; } //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.
[Fact] public async Task GetTest() { //Arrange string numberJson= JsonConvert.SerializeObject(new List<int>() { 1, 2, 3, 4, 5 }); var httpMessageHandler = new Mock<HttpMessageHandler>(); httpMessageHandler.Protected() .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>()) .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.Clear(); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); ValuesController controller = new ValuesController(httpClient); //Act IActionResult result = await controller.Get(); //Assert OkObjectResult resultObject = result as OkObjectResult; Assert.NotNull(resultObject); List<int> numbers = resultObject.Value as List<int>; Assert.Equal(5, numbers.Count); }
Full source code available here.
Good tip. You probably want to edit this sentence:
“The constructor of the controller takes the HttpClient as parameter to the controller constructor by usually by dependency injection.”
Thanks, fixed that sentence.
Hi Brian,
You might also want to consider this library from JustEat:
https://tech.just-eat.com/2017/10/02/reliably-testing-http-integrations-in-a-dotnet-application/
It’s really useful particularly with .net core.
Thanks Sean.
Pingback: Using the HttpClientInterception to Test Methods That Use a HttpClient | no dogma blog
Bryan,
Have you ever encountered the error “System.InvalidOperationException: Handler did not return a response message” when mocking the HttpHandler like this?
What might I be missing?
Thanks,
Phil
Hi Phil,
I have not hit that error. I suggest starting with my solution and work from there.
Bryan