Core Java

How to Delay a Stubbed Method Response With Mockito

In unit testing, Mockito is a popular framework for creating mock objects and defining their behavior. Sometimes, there is a need to simulate delays in the response of a stubbed method, particularly when testing time-sensitive features like retry mechanisms, timeouts, or handling long-running processes. Let us delve into understanding how to introduce a delay in a stubbed method response using Mockito.

1. Understanding the Need for Delay in Stubbed Methods

When testing asynchronous code, retry logic, or timeout scenarios, it’s often essential to simulate a delay in a method’s response. For instance, if you have a method that retries a network call after a failure, you need to simulate a delay in the network response to test this retry behavior properly. Introducing delays in stubbed methods allows you to verify that your code handles these scenarios correctly.

2. Maven Dependencies

To use Mockito and other libraries mentioned in this article, you need to include the following dependencies in your pom.xml file:

<dependencies>
    <!-- Mockito Dependency -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>your_version</version>
        <scope>test</scope>
    </dependency>
    
    <!-- Awaitility Dependency -->
    <dependency>
        <groupId>org.awaitility</groupId>
        <artifactId>awaitility</artifactId>
        <version>your_version</version>
        <scope>test</scope>
    </dependency>
</dependencies>

3. Introduce Delay Using Thread.sleep()

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

3.1 Code Example

import static org.mockito.Mockito.*;

import org.junit.jupiter.api.Test;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DelayTest {

    @Test
    public void testDelayWithThreadSleep() throws Exception {
        // Create a mock object
        MyService myService = mock(MyService.class);

        // Stub the method with a delay using Thread.sleep()
        doAnswer(invocation -> {
            String beforeDelay = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
            System.out.println(beforeDelay + " - Before delay in getResponse()");
            
            Thread.sleep(2000); // Delay of 2 seconds
            
            String afterDelay = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
            System.out.println(afterDelay + " - After delay in getResponse()");
            
            return "Delayed Response";
        }).when(myService).getResponse();

        // Call the method
        String response = myService.getResponse();

        // Get the current timestamp after method returns
        String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());

        // Output the response with timestamp
        System.out.println(timestamp + " - " + response); 
    }
}

3.2 Code Explanation and Output

In the above code, Thread.sleep(2000) introduces a delay of 2 seconds before returning the response “Delayed Response” from the getResponse() method. This allows us to simulate a delayed response scenario.

The output will be:

2024-08-14 12:34:54.123 - Before delay in getResponse()
2024-08-14 12:34:56.123 - After delay in getResponse()
2024-08-14 12:34:56.124 - Delayed Response

4. Introduce Delay Using Mockito’s Answer

Another way to introduce a delay in a stubbed method is by using Mockito’s Answer interface. This allows you to define custom behavior, including delays, when a mock method is called.

4.1 Code Example

import static org.mockito.Mockito.*;

import org.junit.jupiter.api.Test;
import org.mockito.stubbing.Answer;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DelayTest {

    @Test
    public void testDelayWithAnswer() throws Exception {
        // Create a mock object
        MyService myService = mock(MyService.class);

        // Stub the method with a custom Answer
        when(myService.getResponse()).thenAnswer((Answer) invocation -> {
            String beforeDelay = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
            System.out.println(beforeDelay + " - Before delay in getResponse()");

            Thread.sleep(3000); // Delay of 3 seconds

            String afterDelay = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
            System.out.println(afterDelay + " - After delay in getResponse()");

            return "Delayed Response with Answer";
        });

        // Call the method
        String response = myService.getResponse();

        // Get the current timestamp after method returns
        String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());

        // Output the response with timestamp
        System.out.println(timestamp + " - " + response);
    }
}

4.2 Code Explanation and Output

In this example, we use the Answer interface to introduce a delay of 3 seconds before returning “Delayed Response with Answer” from the getResponse() method. This approach gives you more control over the behavior of stubbed methods.

The output will be:

2024-08-14 12:34:54.123 - Before delay in getResponse()
2024-08-14 12:34:57.123 - After delay in getResponse()
2024-08-14 12:34:57.124 - Delayed Response with Answer

5. Introduce Delay Using Awaitility

Awaitility is a library that simplifies testing asynchronous systems. It provides a more readable and flexible way to introduce delays and wait for conditions to be met.

5.1 Code Example

import static org.mockito.Mockito.*;
import static org.awaitility.Awaitility.*;

import org.junit.jupiter.api.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

public class DelayTest {

    @Test
    public void testDelayWithAwaitility() {
        // Create a mock object
        MyService myService = mock(MyService.class);

        // Stub the method
        when(myService.getResponse()).thenReturn("Quick Response");

        // Introduce a delay using Awaitility
        await().atLeast(2, TimeUnit.SECONDS).untilAsserted(() -> {
            String timestampBefore = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
            System.out.println(timestampBefore + " - Before delay in Awaitility");

            String response = myService.getResponse();

            String timestampAfter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
            System.out.println(timestampAfter + " - After delay in Awaitility");

            System.out.println(timestampAfter + " - " + response); // Outputs: Quick Response
        });
    }
}

5.2 Code Explanation and Output

In this example, we use Awaitility to introduce a delay of at least 2 seconds before asserting the response from the getResponse() method. This approach is particularly useful for testing asynchronous behavior and waiting for specific conditions to be met.

The output will be:

2024-08-14 12:34:54.123 - Before delay in Awaitility
2024-08-14 12:34:56.124 - After delay in Awaitility
2024-08-14 12:34:56.124 - Quick Response

6. Conclusion

Delaying a stubbed method’s response can be essential when testing time-sensitive features in your application. This article covered three different approaches to introducing delays in stubbed methods using Thread.sleep(), Mockito’s Answer interface, and the Awaitility library. Each approach has its use cases, and the choice depends on the specific needs of your test scenarios.

6.1 Ensuring Test Stability

When introducing delays in stubbed methods, it’s crucial to ensure that your tests remain stable and reliable. Delays can sometimes lead to flaky tests, where the test results may vary depending on timing and execution conditions. To mitigate this, it’s important to carefully choose the length of the delay, considering the nature of the system under test and the specific scenario being simulated.

Using tools like Awaitility, which allows for flexible waiting conditions, can help in managing delays without hardcoding specific time intervals. Instead of relying solely on fixed delays, you can combine them with conditions that must be met before proceeding. This approach increases the robustness of your tests by reducing the likelihood of false positives or negatives due to timing issues.

Additionally, it’s advisable to run tests multiple times under different conditions (e.g., varying system loads) to verify their consistency. Ensuring that delays are only as long as necessary to simulate real-world scenarios can also help maintain the speed of your test suite while preserving accuracy.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button