单元测试中的 Mock 与 Stub

Java 测试入门

Maria Milusheva

Senior Software Engineer

动机:依赖关系

回顾上一课的依赖流:货币兑换应用依赖银行服务器: 银行演示中依赖流的 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 银行对象不知道如何响应 getRateEuroTo
    double result = exchangeApp.convertEuroTo("USD", 1000);

    assertEquals(1100, result); // 由于缺少设置将失败
}
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 测试入门

Passons à la pratique !

Java 测试入门

Preparing Video For Download...