2024-10-23 013_學寫API_單元測試_MOCK
2024-10-23 013_學寫API_單元測試_MOCK
Mock 是單元測試中經常使用的一種技術,用來模擬(即 “mock”)應用程序中的外部依賴項。它使得我們可以隔離測試的對象,而不需要實際連接到外部系統,如資料庫、網路服務,或者其他無法在單元測試中方便訪問的資源。Mocking 允許測試代碼在沒有這些外部依賴的情況下進行驗證,從而專注於測試特定功能的行為。
為什麼要使用 Mock?
在實際的應用中,API 控制器可能依賴於許多外部資源來完成其工作。例如:
- 資料庫:要從資料庫中讀取和寫入數據。
- 外部服務:與其他微服務或第三方系統進行互動。
- 文件系統:讀取和寫入文件。
在單元測試中,如果我們依賴這些外部資源來進行測試,會有以下幾個問題:
- 不可預測性:外部資源(如資料庫或網路服務)可能會有不可預測的變動,例如數據更改或者網路連線中斷,這會導致測試結果的不穩定。
- 性能問題:訪問外部資源的操作可能非常耗時,使得單元測試變得慢而不易維護。
- 隔離性差:單元測試的目的是要檢查單一方法或單一模組的行為,因此應該將這些外部依賴隔離,保證測試專注於該模組內部的邏輯。
使用 Mock 可以解決以上問題,因為 Mock 是在測試中用來模擬這些外部依賴的假實現,允許測試執行在受控的環境中,且結果是可預測的。
Mock 是如何使用的?
Mock 是通過創建依賴項的假版本來替代實際的依賴項。例如,如果您的控制器需要從資料庫中讀取包裹的信息,在單元測試中您可以使用 Mock 來返回假數據,而不是從資料庫實際查詢。
以下是如何使用 Mock 的一些關鍵概念:
模擬外部依賴項
- 使用 Mock 的方式可以創建控制器所依賴的服務的假實例。
- 例如,在測試
PackageController
時,如果控制器依賴一個資料庫服務IPackageService
,我們可以創建IPackageService
的一個 Mock 版本。
控制輸入和輸出
- 使用 Mock 時,我們可以明確地定義方法應該返回什麼值,以便在測試中控制不同的情景。
- 例如,當呼叫
GetPackageByTrackingNumber("12345")
時,可以讓 Mock 返回一個特定的包裹物件。
驗證交互
- Mock 也可以用來驗證測試代碼是否正確地與外部依賴互動。例如,我們可以檢查
CreatePackage()
方法是否正確地呼叫了一次資料庫的Save()
方法。
- Mock 也可以用來驗證測試代碼是否正確地與外部依賴互動。例如,我們可以檢查
Mock 框架
在 C# 中,我們可以使用一些流行的 Mock 框架來輔助完成這些 Mock 操作,例如:
- Moq:一個非常流行且簡單易用的 Mock 框架。
- NSubstitute:另一個強大且簡單的 Mock 框架。
以 Moq
為例,假設 PackageController
依賴於一個服務 IPackageService
來進行包裹的資料處理。我們可以用 Moq
來模擬這個依賴項:
安裝 Moq:
- 您可以通過 NuGet 安裝
Moq
套件:1
Install-Package Moq
- 您可以通過 NuGet 安裝
使用 Moq 創建 Mock 依賴項:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37using Moq;
using LogisticsAPI.Controllers;
using LogisticsAPI.Models;
using Microsoft.Extensions.Logging;
using Xunit;
public class PackageControllerTests
{
private readonly PackageController _controller;
private readonly Mock<IPackageService> _mockPackageService;
private readonly ILogger<PackageController> _logger;
public PackageControllerTests()
{
_mockPackageService = new Mock<IPackageService>();
_logger = new LoggerFactory().CreateLogger<PackageController>();
// 初始化控制器,並使用 Mock 版本的 IPackageService
_controller = new PackageController(_logger, _mockPackageService.Object);
}
[ ]
public void GetPackageByTrackingNumber_ValidNumber_ReturnsPackage()
{
// Arrange: 設定 mock 依賴,當查詢 tracking number "12345" 時,返回一個假包裹
var package = new Package { TrackingNumber = "12345", Sender = "Alice", Recipient = "Bob", Status = "待派送" };
_mockPackageService.Setup(service => service.GetPackageByTrackingNumber("12345")).Returns(package);
// Act: 呼叫控制器的方法
var result = _controller.GetPackageByTrackingNumber("12345") as OkObjectResult;
// Assert: 驗證結果是否正確
Assert.NotNull(result);
Assert.IsType<Package>(result.Value);
Assert.Equal("12345", ((Package)result.Value).TrackingNumber);
}
}- 解釋:
- **
Mock<IPackageService>
**:創建了一個IPackageService
的 Mock 版本。 - **
Setup()
**:設定當 Mock 的方法被呼叫時的行為,例如當呼叫GetPackageByTrackingNumber("12345")
,我們希望返回一個包裹。 - **
Returns()
**:指定當條件滿足時應返回的假數據。
- **
- 解釋:
Mock 的優勢
- 測試隔離性:您可以在沒有資料庫、沒有網路等外部依賴的情況下進行測試,這樣每個測試都是獨立的,不受外部環境影響。
- 可控性:使用 Mock 可以完全控制測試中不同依賴的行為。例如,當測試異常情況時,可以輕鬆地模擬依賴項拋出例外。
- 驗證交互:Mock 也能幫助您檢查是否與外部依賴進行了正確的交互,例如方法是否被呼叫、呼叫的次數等。
小結
Mock 是單元測試中非常重要的工具,尤其在面對有外部依賴的情況下,它允許開發者通過模擬這些依賴來進行獨立的測試,從而確保測試能夠在受控環境下進行,且能正確驗證應用程式的邏輯。希望這些解釋能幫助您理解什麼是 Mock 以及它的作用!