iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 29
0
Software Development

Dart 語言 - 開啟 Flutter 的鑰匙系列 第 29

Dart 29:Dart 也有 Mockito!

物件導向中,public 函數可能會包含了外部相依,這個外部相依物件有可能是來自外部函式庫,沒有辦法直接修改裡面的數值,在我們要測試的項目中,外部相依就會變成一個不確定的因素。

在不同的程式語言中,都有一個 Mock Framework。Dart 的 Mock framwork 為 Mockito

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 ,另一個則是其他時間時,是否可以正確的回傳相同的時間。

  • 使用 MyClock 的時候,因為跟 CurrentTime 相依,所以測試的時候沒有辦法直接測到所有的 Test Case。
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 上場

Mockito 來製造一個假的 CurrentTime ,這個假的 CurrentTime 可以依照我們的預期傳出回傳值。

  • my_clock_test.dart 底下加上一個 MockCurrentTime 並且使用關鍵字 extends 加上 Mock,並且再利用 implements 實作原本的 CurrentTime
class 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');
  });
  1. 原本代入 MyClock 的 CurrentTime ,用 MockCurrentTime 替換。
  2. 設定 MockCurrentTime 的輸出。
    • when() 函數中設定,需要針對什麼函數設定假資料。 EX: CurrentTime()。
    • 接者,在 when() 後方用 thenReturn() 回傳一個假的的回傳值。
    • 這段 Mock 的程式碼就可以用比較口語的方式設定假資料:當呼叫 currentTime() 時,就回傳 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");
  });
  • 我們將 MockCurrentTime 的回傳值改為 12:00 ,如果沒有意外,結果應該會是輸出 It's noon
  • 測試結果:正確。

Mockito 除了可以模擬假資料外,還可以用來測試函數被呼叫的情況。

verify()

利用 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 時,會覺得它的語法太奇怪了,後來發現,這是為了讓看單元測試的人可以更清楚的知道這個相依物件被模擬成什麼輸出值,用類似說話的方式來定義假資料,能夠在瀏覽的時後更容易了解內容。


上一篇
Day 28:測試你的代碼
下一篇
Day 30:Metadata 以及完賽心得
系列文
Dart 語言 - 開啟 Flutter 的鑰匙30

尚未有邦友留言

立即登入留言