目的
面試的時候被問到要如何做包含外部api的單元測試問題,稍微查一下其實很簡單,怎麼當下答不出來呢? 主要有兩種方式,一種為.net core 2.1以後有提供IHttpClientFactory的介面可以使用。
建立新專案
選擇ASP.NET Core Web API專案範本,並執行下一步
設定新的專案
命名你的專案名稱,並選擇專案要存放的位置。
其他資訊
直接進行下一步
建立資料夾
編輯Program.cs檔案
註冊AddHttpClient。
builder.Services.AddHttpClient();
新增一個類別檔
- 加入前
- 加入後
建構子注入
將註冊的httpclient透過建構子注入
readonly IHttpClientFactory _httpClientFactory;
public CallAPIServices(IHttpClientFactory httpClientFactory) {
_httpClientFactory = httpClientFactory;
}
- 加入前
- 加入後
新增方法
新增一個會去發外部請求的方法,模擬當有包含第三方api時如何測試
public async Task<string> Get() {
var client = _httpClientFactory.CreateClient();
var response = await client.GetAsync("https://example.com");
if (response.IsSuccessStatusCode) {
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
return "";
}
新增測試專案
對方案點選右鍵>加入>新增專案
設定新的專案
替測試專案命名,建議命名規則以.Tests做結尾
其他資訊
專案架構需要與要測試的專案相同
加入參考
將要測試的專案加入測試專案
對CallAPIServices.Tests點選右鍵>加入>專案參考
NuGet加入套件
透過NuGet安裝Moq套件至測試專案
新增對應資料夾與測試檔案
新增檔案,檔名需要與要測試的檔案一致,並加上Tests
新增程式碼
在新增的檔案內新增一個方法並寫入程式碼,一開始會看到許多紅色波浪,是因為沒有加入參考
//定義這個方法是不需要傳入參數的單元測試,另一種為[Theory],詳細說明可看相關參考5
[Fact]
//命名規則 => 類別名稱_方法名稱_要測試的方法行為_回傳狀態
public async Task CallAPIServices_Get_HttpClient_Success() {
//模擬出一個IHttpClientFactory的假物件
var mockFactory = new Mock<IHttpClientFactory>();
//產生http回傳的實例
HttpResponseMessage result = new HttpResponseMessage() {
StatusCode = HttpStatusCode.OK,
Content = new StringContent("{'account':'bill','age':18}")
};
//模擬出一個http處理程序的基本類型實例的假物件
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
//模擬http發出請求後所做的一連串事情,最後並回傳結果
//protected => 發出請求的方法SendAsync是一個protected存取修飾詞,不能直接存取,所以告訴moq請幫忙執行protected方法,詳細說明可看參考6
//Setup => 設定此執行動作為SendAsync方法,並需要兩個傳入參數,HttpRequestMessage與CancellationToken
//ItExpr.IsAny => 請moq協助傳入一個參數為HttpRequestMessage與CancellationToken的假參數,另一種為It.IsAny是非protected方法使用
//ReturnsAsync => 回傳非同步的值
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(result);
//將製作好的假請求與回傳給HttpClient
var client = new HttpClient(mockHttpMessageHandler.Object);
//模擬實際請求,並接收到我們所製作的假物件
mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(client);
//將製作好的完整請求流程給我們在使用的方法
var services = new HttpclientExample.Services.CallAPIServices(mockFactory.Object);
//執行要測試的方法
var response = await services.Get();
//回傳值不能為null
Assert.NotNull(response);
//回傳值必須一致
Assert.Equal("{'account':'bill','age':18}", response);
}
- 加入前
- 加入後
執行測試
點右鍵執行測試,最後結果會如左下角顯示成功
相關參考
單元測試好的做法 為什麼要使用IHttpClientFactory moq套件介紹 單元測試介紹 Fact跟Theory差別 存取修飾詞