物件導向中,public 函數可能會包含了外部相依,這個外部相依物件有可能是來自外部函式庫,沒有辦法直接修改裡面的數值,在我們要測試的項目中,外部相依就會變成一個不確定的因素。
在不同的程式語言中,都有一個 Mock Framework。Dart 的 Mock framwork 為 Mockito
。
Mockito 是 Java 的 Mock framework 之一,在寫 Java 單元測試的開發者,一定都有使用過它,它可以讓我們輕鬆的在單元測試中模擬相依物件。Dart 團隊將 Mockito 用 Dart 改寫,並加入至 Dart 的 package 中。
假設我們有一個類別 MyClock
:
有一個函數會呼叫由建構子帶進來的 CurrentTime
,並根據 CurrentTime 回傳的時間,顯示不同的內容。
class MyClock{
CurrentTime currentTime;
MyClock(this.currentTime);
String call(){
var time = currentTime();
if(time == '12:00'){
return "It's noon";
}
return time;
}
}
→ 測試案例 (Test Case) 分析,以這個函式來說,會需要兩個測試案例:一個是測試時間為 12:00時,是否會回傳 It's noon
,另一個則是其他時間時,是否可以正確的回傳相同的時間。
void main(){
test('MyClock should return proper time', (){
final currentTime = CurrentTime();
final myClock = MyClock(currentTime);
final result = myClock();
expect(result, '13:00');
});
}
→ 測試結果為失敗,因為現在的時間並不是13:00。
用 Mockito
來製造一個假的 CurrentTime ,這個假的 CurrentTime 可以依照我們的預期傳出回傳值。
my_clock_test.dart
底下加上一個 MockCurrentTime 並且使用關鍵字 extends
加上 Mock,並且再利用 implements
實作原本的 CurrentTimeclass MockCurrentTime extends Mock implements CurrentTime{}
test('MyClock should return proper time', (){
final currentTime = MockCurrentTime();
when(currentTime()).thenReturn('13:00');
final myClock = MyClock(currentTime);
final result = myClock();
expect(result, '13:00');
});
when()
函數中設定,需要針對什麼函數設定假資料。 EX: CurrentTime()。when()
後方用 thenReturn()
回傳一個假的的回傳值。13:00
。test("MyClock should return it's noon when current time is 12:00", (){
final currentTime = MockCurrentTime();
when(currentTime()).thenReturn('12:00');
final myClock = MyClock(currentTime);
final result = myClock();
expect(result, "It's noon");
});
12:00
,如果沒有意外,結果應該會是輸出 It's noon
。Mockito 除了可以模擬假資料外,還可以用來測試函數被呼叫的情況。
利用 verify() 函數可以判斷 Mock 的函數,是否如預期般被呼叫。
接上例,我們預期每一次呼叫 MyClock 都只會呼叫一次 CurrentTime,我們可以這樣寫:
test('MyClock should return proper time', (){
final currentTime = MockCurrentTime();
when(currentTime()).thenReturn('13:00');
final myClock = MyClock(currentTime);
final result = myClock();
verify(currentTime()).called(1);
expect(result, '13:00');
});
發現什麼?
verify()
函數中代入了 mock 的函數,接者在後方串接 called()
。called()
裡面代入了 1
,代表該函數被呼叫一次。在單元測試的領域中,相依物件一直都是最頭痛的問題,使用 Mockito 可以在相依物件的問題上得到一條救命繩索。
無論相依物件是來自第三方的函式庫還是自己程式其他類別,都可以使用 Mockito 來造假,接者我們就可以在測試案例中自由的使用這個假的相依物件。
初次學習 Mockito 時,會覺得它的語法太奇怪了,後來發現,這是為了讓看單元測試的人可以更清楚的知道這個相依物件被模擬成什麼輸出值,用類似說話的方式來定義假資料,能夠在瀏覽的時後更容易了解內容。