Introduction
This article explains how to mock the HttpClient using XUnit. Yes, we already have few ways to mock httpclient by writing a wrapper for HttpClient. But there is a problem for not covering test cases for HttpClient class, since we know there isn't an interface inherited with HttpClient. To handle this case, we introduced HttpClient property in the interface. Using this interface property we can achieve whatever we want around httpclient mocking.
Problem Statement
As I already mentioned, HttpClient does not inherit from any interface, so you will have to write your own. A few of them suggest a decorator-like pattern.
So, here we are mocking only wrapper class and not httpclient. So how can we mock httpclient as well? Let's see it in action, I am using Visual Studio 2019, .Net Core 2.0, and XUnit.
Step 1
To demonstrate httpclient mocking using Xunit, I am creating a simple web API application and adding a new test(XUnit) project.
Step 2
Let's introduce IHttpClientHelper interface to mock httpclient. Here you can see HttpClient property as well, which is used to hold the mocked HttpMessageHandler object.
The implementation class for IHttpClientHelper looks like below:
- using System;
- using System.Net.Http;
- using System.Net.Http.Headers;
- using System.Threading.Tasks;
-
- namespace Demo.Services
- {
-
- public class HttpClientHelper : IHttpClientHelper
- {
- public HttpClient HttpClient { get; set; }
-
- public async Task<TResult> GetAsync<TResult>(string requestUri)
- {
- TResult objResult = default(TResult);
-
- using (var client = this.GetHttpClient())
- {
- using (var response = await client.GetAsync(requestUri))
- {
- if (TryParse<TResult>(response, out objResult))
- {
- return objResult;
- }
-
- using (HttpContent content = response.Content)
- {
- throw new HttpRequestException(response.Content.ReadAsStringAsync().Result);
- }
- }
- }
- }
- private HttpClient GetHttpClient()
- {
- if (HttpClient == null)
- {
- var _httpClient = new HttpClient();
- _httpClient.DefaultRequestHeaders.Accept.Clear();
- _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
- return _httpClient;
- }
- return HttpClient;
- }
-
- private bool TryParse<TResult>(HttpResponseMessage response, out TResult t)
- {
- if (typeof(TResult).IsAssignableFrom(typeof(HttpResponseMessage)))
- {
- t = (TResult)Convert.ChangeType(response, typeof(TResult));
- return true;
- }
-
- if (response.IsSuccessStatusCode)
- {
- t = response.Content.ReadAsAsync<TResult>().Result;
- return true;
- }
-
- t = default(TResult);
- return false;
- }
- }
- }
Step 3
Alright, we have implemented wrapper class for HttpClient. Now let's move into Test project and write test cases for above GetAsync() method. We will see how to mock the HttpClient using the interface property.
- using AutoFixture;
- using Demo.Models;
- using Demo.Services;
- using Moq;
- using Moq.Protected;
- using System;
- using System.Collections.Generic;
- using System.Net;
- using System.Net.Http;
- using System.Net.Http.Headers;
- using System.Threading;
- using System.Threading.Tasks;
- using Xunit;
-
- namespace Demo.Test
- {
- public class HttpClientHelperTest
- {
- protected HttpClientHelper HttpClientHelperUnderTest { get; }
- public HttpClientHelperTest()
- {
- HttpClientHelperUnderTest = new HttpClientHelper();
- }
-
-
-
-
- public class GetAsyncHttpHelper : HttpClientHelperTest
- {
- [Fact]
- public async Task When_GetAsync_Returns_Success_Result()
- {
-
- var result = new List<Weather>() {
- new Weather() { Description="Test",Temp_max=1.1, Temp_min=1.1 }
- };
- var httpMessageHandler = new Mock<HttpMessageHandler>();
- var fixture = new Fixture();
-
-
- httpMessageHandler.Protected()
- .Setup<Task<HttpResponseMessage>>(
- "SendAsync",
- ItExpr.IsAny<HttpRequestMessage>(),
- ItExpr.IsAny<CancellationToken>()
- )
- .ReturnsAsync((HttpRequestMessage request, CancellationToken token) =>
- {
- HttpResponseMessage response = new HttpResponseMessage();
- response.StatusCode = System.Net.HttpStatusCode.OK;
- response.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(result));
- response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
- return response;
- });
-
- var httpClient = new HttpClient(httpMessageHandler.Object);
- httpClient.BaseAddress = fixture.Create<Uri>();
-
- HttpClientHelperUnderTest.HttpClient = httpClient;
-
-
- var weatherResult = await HttpClientHelperUnderTest.GetAsync<List<Weather>>(string.Empty);
-
-
- Assert.NotNull(weatherResult);
- }
- }
- }
- }
Here you can see mocking the HttpMessageHandler and assigning it to a HttpClient constructor.
SendAsync() is default implementation for all the HttpClient actions.
- public class HttpClient : HttpMessageInvoker
HttpMessageInvoker class looks like below.
Step 4
Let's quickly check the test case results:
Note
I have attached a sample project with this article. If you want more explanation or a live example, please download the project and refer to that.
Reference
https://dejanstojanovic.net/aspnet/2020/march/mocking-httpclient-in-unit-tests-with-moq-and-xunit/
I hope this was helpful to you. Enjoy ;)