1. 简介

Mockito 允许开发者模拟方法返回特定值或执行特定动作。在某些测试场景中,我们需要在模拟方法响应中加入延迟,以模拟真实环境中的网络延迟或慢速数据库查询等条件。

本教程将探索使用 Mockito 引入此类延迟的多种方法。

2. 理解模拟方法中延迟的必要性

在模拟方法中引入延迟在以下场景特别有用

  • 模拟真实环境:测试应用如何处理延迟和超时
  • 性能测试:确保应用能优雅处理慢响应
  • 调试:重现与时间同步相关的诊断问题

在测试中加入延迟有助于构建更健壮、更具弹性的应用,使其更好地应对真实场景。

3. Maven 依赖

将以下依赖添加到项目中:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.11.0</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.awaitility</groupId>
    <artifactId>awaitility</artifactId>
    <version>4.2.1</version>
    <scope>test</scope>
</dependency>

4. 测试环境准备

创建一个 PaymentService 类模拟支付处理:

public class PaymentService {
    public String processPayment() {
        // 模拟支付处理并返回完成状态
        return "SUCCESS";
    }
}

后续将用多种方式模拟支付处理的延迟。

5. 使用 Thread.sleep() 引入延迟

最直接的延迟方式是在模拟方法中使用 Thread.sleep()。该方法会让当前线程暂停指定毫秒数:

@Test
public void whenProcessingPayment_thenDelayResponseUsingThreadSleep() {
    when(paymentService.processPayment()).thenAnswer(invocation -> {
        Thread.sleep(1000); // 延迟1秒
        return "SUCCESS";
    });

    long startTime = System.currentTimeMillis();
    String result = paymentService.processPayment();
    long endTime = System.currentTimeMillis();

    assertEquals("SUCCESS", result);
    assertTrue((endTime - startTime) >= 1000); // 验证延迟
}

⚠️ 踩坑提示:此方法简单粗暴,但会阻塞线程,不适合测试异步操作或生产环境使用。

6. 使用 Mockito 的 Answer 引入延迟

Mockito 的 Answer 接口提供了更灵活的模拟方式。我们可以用它实现延迟和其他自定义行为:

@Test
public void whenProcessingPayment_thenDelayResponseUsingAnswersWithDelay() throws Exception {
    when(paymentService.processPayment()).thenAnswer(AdditionalAnswers.answersWithDelay(1000, invocation -> "SUCCESS"));

    long startTime = System.currentTimeMillis();
    String result = paymentService.processPayment();
    long endTime = System.currentTimeMillis();

    assertEquals("SUCCESS", result);
    assertTrue((endTime - startTime) >= 1000); // 验证延迟
}

这里使用 AdditionalAnswers.answersWithDelay() 在返回 "SUCCESS" 前引入1秒延迟。✅ 优势:抽象了延迟逻辑,代码更简洁易维护。

7. 使用 Awaitility 引入延迟

Awaitility 是测试中同步异步操作的 DSL 工具。它既能等待条件满足,也能引入延迟,特别适合测试异步代码:

@Test
public void whenProcessingPayment_thenDelayResponseUsingAwaitility() {
    when(paymentService.processPayment()).thenAnswer(invocation -> {
        Awaitility.await().pollDelay(1, TimeUnit.SECONDS).until(()->true);
        return "SUCCESS";
    });

    long startTime = System.currentTimeMillis();
    String result = paymentService.processPayment();
    long endTime = System.currentTimeMillis();

    assertEquals("SUCCESS", result);
    assertTrue((endTime - startTime) >= 1000); // 验证延迟
}

Awaitility.await().pollDelay(1, TimeUnit.SECONDS).until(() -> true) 至少延迟1秒后返回。✅ 优势:流式API清晰易读,提供强大的异步等待功能。

8. 确保测试稳定性

引入延迟时,遵循以下最佳实践可确保测试套件稳定性:

  • 设置合理超时:超时需足够容纳延迟,但避免过长影响测试执行时间
  • 模拟外部依赖:优先模拟外部依赖以可靠控制延迟
  • 隔离延迟测试:将延迟测试分组隔离,避免影响整体测试套件执行时间

9. 总结

在 Mockito 中延迟模拟方法响应,对模拟真实环境、性能测试和调试都很有价值。

本文通过 Thread.sleep()Awaitility 和 Mockito 的 Answer 三种方式实现了延迟。关键点:确保测试稳定性对构建可靠测试至关重要,这些技术能帮助创建更适应真实场景的弹性应用。

完整代码示例请查看 GitHub


原始标题:How to Delay a Stubbed Method Response With Mockito | Baeldung