原文地址: How to unit test a class that consumes an HttpClient with IHttpClientFactory in ASP.NET Core?
做者: Anthony Giretti
譯者: Lamond Lugit
幾年前,微軟引入了HttpClient
類來替代HttpWebRequest
來發送Web請求。這個新的類更易於使用,更加簡潔,更具備異步性,且易於擴展。github
HttpClient
類有一個能夠接受HttpMessageHandler
類對象的構造函數。HttpMessageHandler
類對象能夠接受一個請求(HttpRequestMessage
), 並返回響應(HttpResponseMessage
)。它的功能徹底取決於它的實現。默認狀況下HttpClient
使用的是HttpClientHandler
,HttpClientHandler
是一個處理程序,它向網絡服務器發送請求並從服務器返回響應。在本篇博文中,咱們將經過繼承DelegatingHandler
來建立本身的HttpMessageHandler
。json
爲了實現以上功能,HttpClient
對象不能夠直接使用,而是須要與容許使用IHttpClientFactory
接口進行模擬的依賴注入一塊兒使用。c#
HttpMessageHandler
下面的例子中,咱們只討論HttpResponseMessage
, 不會處理HttpRequestMessage
。服務器
如下是我僞造的一個HttpMessageHandler
對象。網絡
public class FakeHttpMessageHandler : DelegatingHandler { private HttpResponseMessage _fakeResponse; public FakeHttpMessageHandler(HttpResponseMessage responseMessage) { _fakeResponse = responseMessage; } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return await Task.FromResult(_fakeResponse); } }
這裏我添加了一個須要HttpResponseMessage
構造函數,而後複寫了SendAsync
方法, 在該方法中直接返回了構造函數中傳入的HttpResponseMessage
對象。app
IHttpClientFactory
接口的服務下面咱們須要編寫一個UserService
類,這個類提供了一個GetUsers
方法,來從遠程服務器端獲取用戶列表。異步
public class UserService { private readonly IHttpClientFactory _httpFactory; public UserService(IHttpClientFactory httpFactory) { _httpFactory = httpFactory; } public async Task<List<User>> GetUsers(string url) { using (HttpClient httpclient = _httpFactory.CreateClient()) { using (HttpResponseMessage response = await httpclient.GetAsync(url)) { if (response.StatusCode == HttpStatusCode.OK) { List<User> users = await response.Content.ReadAsAsync<List<User>>(); return users; } return null; } } } }
如下是Api請求返回的用戶類async
public class User { public string FirstName { get; set; } public string LastName { get; set; } }
如你所見,使用HttpClientFactory
容許咱們模擬HttpClient
實例化ide
在下面的單元測試中,咱們會使用XUnit
、FluentAssertion
、NSubstitute
public class UserServiceTests { [Fact] public async Task WhenACorrectUrlIsProvided_ServiceShouldReturnAlistOfUsers() { // Arrange var users = new List<User> { new User { FirstName = "John", LastName = "Doe" }, new User { FirstName = "John", LastName = "Deere" } }; var httpClientFactoryMock = Substitute.For<IHttpClientFactory>(); var url = "http://good.uri"; var fakeHttpMessageHandler = new FakeHttpMessageHandler(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(users), Encoding.UTF8, "application/json") }); var fakeHttpClient = new HttpClient(fakeHttpMessageHandler); httpClientFactoryMock.CreateClient().Returns(fakeHttpClient); // Act var service = new UserService(httpClientFactoryMock); var result = await service.GetUsers(url); // Assert result .Should() .BeOfType<List<User>>() .And .HaveCount(2) .And .Contain(x => x.FirstName == "John") .And .Contain(x => x.LastName == "Deere") .And .Contain(x => x.LastName == "Doe"); } }
HttpClient
對象,而後定義了咱們指望的獲得的僞造對象httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);
public class UserServiceTests { [Fact] public async Task WhenABadUrlIsProvided_ServiceShouldReturnNull() { // Arrange var httpClientFactoryMock = Substitute.For<IHttpClientFactory>(); var url = "http://bad.uri"; var fakeHttpMessageHandler = new FakeHttpMessageHandler(new HttpResponseMessage() { StatusCode = HttpStatusCode.NotFound }); var fakeHttpClient = new HttpClient(fakeHttpMessageHandler); httpClientFactoryMock.CreateClient().Returns(fakeHttpClient); // Act var service = new UserService(httpClientFactoryMock); var result = await service.GetUsers(url); // Assert result .Should() .BeNullOrEmpty(); } }
本篇做者講解了在ASP.NET Core中如何僞造HttpClient
來測試持有HttpClient
對象的類。這裏主要是經過僞造的DelegatingHandler
對象來建立一個HttpClient
對象,並使用IHttpClientFactory
來獲取僞造的HttpClient
來達到目的。