昨天寫了一個單元測試之後,今天要進一步的講 Mock 。
現在我們有 Public 的 A方法,與 Public 的 B方法。
在 A方法中,會另外呼叫 B方法來取得資料庫資料。
當你想要測試 A方法的話,在執行的過程中、一定會執行 B方法、去資料庫要資料。
而我們寫的單元測試的目的是想要驗證 A方法正不正確。畢竟單元測試的就是以驗證一個方法為單位。
針對 A方法的單元測試就只管 A方法, B方法應由針對 B方法的單元測試做驗證。
要做隔離開來,就是 A方法的話,在執行測試的過程中,當要執行 B方法時,拿到的資料是我們的假資料,又稱作 Mock 資料,就是模仿的意思。
我們會使用 Mock 框架提供假資料給我們方法。
今天要使用的是 .NET 上的 Moq 框架,來幫我們做 Mock。
使用 NuGet 幫我們的單元測試專案進行安裝
這邊有個 LoginService 要做測試,但他會使用 UserService 這個外部方法,我們要靠 Mock 來幫我們提供 UserService 的假資料的話,我們要先把 UserService 改用 Interface 的方式從外部注入(有 IOC 相依於介面的概念有關)
原版的 LoginService 使用 UserService 的方式
改為外部注入後如下:
public class LoginService
{
private readonly IUserService _userService;
public LoginService(IUserService service)
{
_userService = service;
}
public bool Login(string email, string password)
{
var user = _userService.GetUser(email, password);
return user != null;
}
}
IUserService 與 UserService 程式碼如下:
現在我們 LoginService 所使用的 UserService 是從外部傳進去的。我們就能用 Mock 一個 UserService。
Mock 是用相依於介面的概念幫我們產生符合 IUserService 介面的假 UserService。
所以要使用的是 IUserService,你用 UserService 會執行錯誤。
var mockService = new Mock<IUserService>();
// 定義好你要使用的方法,與其要回傳的物件
mockService.Setup(g => g.GetUser(email, password)).Returns(new User());
完整的測試如下
[TestMethod()]
public void LoginSuccessTest()
{
// Arrange
var email = "test@test.com";
var password = "長庚好帥";
var mockService = new Mock<IUserService>();
mockService.Setup(g => g.GetUser(email, password))
.Returns((User)new User());
var loginService = new LoginService(mockService.Object);
// Act
var loginResult = loginService.Login(email, password);
// Assert
Assert.IsTrue(loginResult);
}
mockService.Setup().Returns()
中,Returns()
內我們要回傳的結果。你在測試的時候可以改變一下回傳的內容。來強制測試不會通過。
就個人經驗,常常會發生你的單元測試寫錯了,但是你的執行結果是成功(綠燈),你會誤以為你的程式是正確的。