ASP.NET Core針對(duì)一個(gè)使用HttpClient對(duì)象的類(lèi)編寫(xiě)單元測(cè)試詳解
介紹
幾年前,微軟引入了HttpClient類(lèi)來(lái)替代HttpWebRequest來(lái)發(fā)送Web請(qǐng)求。這個(gè)新的類(lèi)更易于使用,更加簡(jiǎn)潔,更具有異步性,且易于擴(kuò)展。
HttpClient類(lèi)有一個(gè)可以接受HttpMessageHandler類(lèi)對(duì)象的構(gòu)造函數(shù)。HttpMessageHandler類(lèi)對(duì)象可以接受一個(gè)請(qǐng)求(HttpRequestMessage), 并返回響應(yīng)(HttpResponseMessage)。它的功能完全取決于它的實(shí)現(xiàn)。默認(rèn)情況下HttpClient使用的是HttpClientHandler,HttpClientHandler是一個(gè)處理程序,它向網(wǎng)絡(luò)服務(wù)器發(fā)送請(qǐng)求并從服務(wù)器返回響應(yīng)。在本篇博文中,我們將通過(guò)繼承DelegatingHandler來(lái)創(chuàng)建自己的HttpMessageHandler。
為了實(shí)現(xiàn)以上功能,HttpClient對(duì)象不可以直接使用,而是需要與允許使用IHttpClientFactory接口進(jìn)行模擬的依賴(lài)注入一起使用。
讓我們來(lái)偽造一個(gè)HttpMessageHandler
下面的例子中,我們只討論HttpResponseMessage, 不會(huì)處理HttpRequestMessage。
以下是我偽造的一個(gè)HttpMessageHandler對(duì)象。
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); } }
這里我添加了一個(gè)需要HttpResponseMessage構(gòu)造函數(shù),然后復(fù)寫(xiě)了SendAsync方法, 在該方法中直接返回了構(gòu)造函數(shù)中傳入的HttpResponseMessage對(duì)象。
編寫(xiě)一個(gè)使用IHttpClientFactory接口的服務(wù)
下面我們需要編寫(xiě)一個(gè)UserService類(lèi),這個(gè)類(lèi)提供了一個(gè)GetUsers方法,來(lái)從遠(yuǎn)程服務(wù)器端獲取用戶(hù)列表。
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請(qǐng)求返回的用戶(hù)類(lèi)
public class User { public string FirstName { get; set; } public string LastName { get; set; } }
如你所見(jiàn),使用HttpClientFactory允許我們模擬HttpClient實(shí)例化
測(cè)試服務(wù)
在下面的單元測(cè)試中,我們會(huì)使用XUnit、FluentAssertion、NSubstitute
測(cè)試場(chǎng)景1: 模擬一個(gè)請(qǐng)求,返回2個(gè)用戶(hù)
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"); } }
- 在以上測(cè)試中,我們期望獲取一個(gè)成功的響應(yīng),并得到2個(gè)用戶(hù)的信息。
- 我們期望從Service中得到的數(shù)據(jù)是JSON格式的。
- 我們使用一個(gè)偽造的處理程序初始化了一個(gè)HttpClient對(duì)象,然后定義了我們期望的得到的偽造對(duì)象httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);
測(cè)試場(chǎng)景2: 模擬一個(gè)404錯(cuò)誤,返回空數(shù)據(jù)
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(); } }
和測(cè)試場(chǎng)景1類(lèi)似,當(dāng)一個(gè)Http請(qǐng)求返回Not Found, 它的結(jié)果集是Null
總結(jié)
本篇作者講解了在ASP.NET Core中如何偽造HttpClient來(lái)測(cè)試持有HttpClient對(duì)象的類(lèi)。這里主要是通過(guò)偽造的DelegatingHandler對(duì)象來(lái)創(chuàng)建一個(gè)HttpClient對(duì)象,并使用IHttpClientFactory來(lái)獲取偽造的HttpClient來(lái)達(dá)到目的。
本篇源代碼:https://github.com/lamondlu/Sample_TestHttpClient (本地下載)
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
原文地址: How to unit test a class that consumes an HttpClient with IHttpClientFactory in ASP.NET Core?
作者: Anthony Giretti
相關(guān)文章
ASP.NET?MVC使用jQuery的Load方法加載靜態(tài)頁(yè)面及注意事項(xiàng)
這篇文章介紹了ASP.NET?MVC使用jQuery加載靜態(tài)頁(yè)面的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09asp.net 數(shù)據(jù)庫(kù)的連接和datatable類(lèi)
asp.net下數(shù)據(jù)庫(kù)的連接與數(shù)據(jù)庫(kù)datatable類(lèi)實(shí)現(xiàn)代碼。2009-05-05datagrid綁定list沒(méi)有數(shù)據(jù) 表頭不顯示的解決方法
datagrid綁定list沒(méi)有數(shù)據(jù) 表頭不顯示的問(wèn)題,那是因?yàn)?綁定了null,你給list new一下就好 表頭就會(huì)有啦2013-05-05Win2008 server + IIS7 設(shè)置身份模擬(ASP.NET impersonation)
IIS7 與 IIS 6 相比有了很大的改動(dòng),原來(lái)在 IIS 6 下可以的設(shè)置到了 IIS 7 下有的會(huì)發(fā)生變化。身份模擬的配置上,IIS7 和 IIS6有很大不同,網(wǎng)上IIS6的身份模擬的文章比較多,但介紹IIS7的比較少,我把的一些折騰的經(jīng)驗(yàn)在這篇博客中寫(xiě)下來(lái),以供參考2011-10-10關(guān)于ASP.NET中TreeView用法的一個(gè)小例子
下面是一個(gè)Treeview動(dòng)態(tài)的綁定3層深度的樹(shù)的代碼,有需要的朋友可以參考一下2013-12-12用WPF實(shí)現(xiàn)屏幕文字提示的實(shí)現(xiàn)方法
本文介紹WPF應(yīng)用程序?qū)崿F(xiàn)在屏幕上顯示一行或多行文字通知。它沒(méi)有標(biāo)題欄和最大化最小化等按鈕,可以有半透明背景以使文字的顯示更清晰,鼠標(biāo)點(diǎn)擊后提示消失。2013-07-07ASP.NET文件上傳Upload的實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了ASP.NET文件上傳的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11