1. Introduction

Mockito allows developers to stub methods to return specific values or perform certain actions. In some test scenarios, we may need to introduce a delay in the response of a stubbed method to simulate real-world conditions, such as network latency or slow database queries.

In this tutorial, we’ll explore different ways to introduce such delays using Mockito.

2. Understanding the Need for Delay in Stubbed Methods

Introducing a delay in stubbed methods can be beneficial in several situations:

  • Simulating real-world conditions: Testing how the application handles delays and timeouts
  • Performance testing: Ensuring the application can handle slow responses gracefully
  • Debugging: Reproducing and diagnosing issues related to timing and synchronization

Adding delays in our tests can help us to create more robust and resilient applications that are better prepared for real-world scenarios.

3. Maven Dependencies

Let’s add the Maven dependencies for mockito-core and awaitility to our project:

<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. Setup

Let’s create a PaymentService to simulate processing payments:

public class PaymentService {
    public String processPayment() {
        // simulate processing payment and return completion status
        return "SUCCESS";
    }
}

We’ll also introduce a delay to simulate processing the payment in various ways.

5. Introduce Delay Using Thread.sleep()

The simplest way to introduce a delay is by using Thread.sleep() within the stubbed method. This method pauses the execution of the current thread for a specified number of milliseconds:

@Test
public void whenProcessingPayment_thenDelayResponseUsingThreadSleep() {
    when(paymentService.processPayment()).thenAnswer(invocation -> {
        Thread.sleep(1000); // Delay of one second
        return "SUCCESS";
    });

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

    assertEquals("SUCCESS", result);
    assertTrue((endTime - startTime) >= 1000); // Verify the delay
}

In this example, the processPayment() method of the PaymentService mock will wait for two seconds before returning “SUCCESS”. This method is straightforward but blocks the thread, which might not be suitable for testing asynchronous operations or for use in a production environment.

6. Introduce Delay Using Mockito’s Answer

Mockito’s Answer interface provides a more flexible way to stub methods. We can use it to introduce delays along with other custom behaviors:

@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); // Verify the delay
}

In this example, the answersWithDelay() method from Mockito’s AdditionalAnswers class is used to introduce a delay of 2 seconds before returning “SUCCESS”. This approach abstracts the delay logic, making the code cleaner and easier to maintain.

7. Introduce Delay Using Awaitility

Awaitility is a DSL for synchronizing asynchronous operations in tests. It can be used to wait for conditions to be met, but it can also introduce delays. This makes it particularly useful for testing asynchronous code:

@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); // Verify the delay
}

In this example, Awaitility.await().pollDelay(1, TimeUnit.SECONDS).until(() -> true) introduces a delay of at least one second before returning “SUCCESS”. Awaitility’s fluent API makes it easy to read and understand, and it provides powerful features for waiting on asynchronous conditions.

8. Ensuring Test Stability

To ensure test suite stability when introducing delays, consider the following best practices:

  • Set appropriate timeouts: Ensure timeouts are generous enough to accommodate delays but not too long to impact test execution time. This prevents tests from hanging indefinitely if something goes wrong.
  • Mock external dependencies: When possible, mock external dependencies to control and simulate delays reliably.
  • Isolate delayed tests: Isolate tests with delays to prevent them from affecting the overall test suite execution time. This can be done by grouping such tests into a separate category or running them in a different environment.

9. Conclusion

Delaying the response of stubbed methods in Mockito is useful for simulating real-world conditions, testing performance, and debugging.

In this article, we introduced these delays effectively by using Thread.sleep(), Awaitility, and Mockito’s Answer. Additionally, ensuring test stability is crucial for maintaining reliable, robust tests, and incorporating these techniques can help create more resilient applications prepared for real-world scenarios.

As always the code is available over on GitHub.