單元測試中的 Mock 與 Stub

Java 測試入門

Maria Milusheva

Senior Software Engineer

動機:相依性

回想上一課的相依流程:匯率換算 App 相依於銀行伺服器: 銀行示範中的相依流程 Mermaid 圖

無法驗證計算,因為匯率本質上不可預測

Java 測試入門

測試中的相依管理

Mocking —— 為測試中難以使用的真實物件建立模擬物件或行為,讓你在不依賴外部相依的情況下進行測試。


import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

Mock 範例

Java 測試入門

Mocking

  • Mock=就像替身演員,只在測試情境中扮演真實物件

回到上一課:

public double convertEuroTo(String currency, double amount) {
    double rate = this.bank.getRateEuroTo(currency);
    return amount * rate;
}

➡ 為測試建立可預期的 EuropeanCentralBankServer 物件

Java 測試入門

Mocking:匯率換算

使用 mock 進行測試如下:

@Test
void convert_returnsExpectedValue() {
    EuropeanCentralBankServer bank = mock(EuropeanCentralBankServer.class);
    ExchangeApp exchangeApp = new ExchangeApp(bank);

// 需要為 mock 設定行為 when(bank.getRateEuroTo("USD")).thenReturn(1.1);
// 從 mock 物件呼叫 getRateEuroTo,取得預先設定的值 double result = exchangeApp.convertEuroTo("USD", 1000); assertEquals(1100, result); // 現在可對精確回傳值進行斷言 }
Java 測試入門

常見錯誤

別忘了為 mock 設定行為:

@Test
void convert_returnsExpectedValue() {
    EuropeanCentralBankServer bank = mock(EuropeanCentralBankServer.class);
    ExchangeApp exchangeApp = new ExchangeApp(bank);

    // 未設定 mock 行為——mock 的 bank 物件不知道如何回應 getRateEuroTo
    double result = exchangeApp.convertEuroTo("USD", 1000);

    assertEquals(1100, result); // 由於未設定 mock,測試會失敗
}
Java 測試入門

Stubbing

測試失敗如下:

org.opentest4j.AssertionFailedError: 
Expected :1100.0
Actual   :0.0

為 mock 設定行為稱為stubbing

Stubbing —— 在測試期間,對呼叫提供最小且預先定義的回應;對未設定的呼叫通常不回應。

Java 測試入門

Mockito 的例外處理

Mockito 也能模擬錯誤情境:

EuropeanCentralBankServer bank = mock(EuropeanCentralBankServer.class);
ExchangeApp exchangeApp = new ExchangeApp(bank);
// 改用 thenThrow() 取代 thenReturn()
when(bank.getRateEuroTo("USD"))
        .thenThrow(new RuntimeException("Bank server unavailable."));

exchangeApp.convertEuroTo("USD", 1000); // getRateEuroTo 會拋出例外
Java 測試入門

一起來練習吧!

Java 測試入門

Preparing Video For Download...