Using Mockito to Return Different Results from Multiple Calls to the Same Method

Nov 17, 2025 · Programming · 22 views · 7.8

Keywords: Mockito | Unit Testing | Method Mocking

Abstract: This article explores how to configure mocked methods in Mockito to return different results on subsequent invocations. Through detailed analysis of thenReturn chaining and thenAnswer custom logic, combined with ExecutorCompletionService testing scenarios, it demonstrates effective simulation of non-deterministic responses. The article includes comprehensive code examples and best practice recommendations to help developers write more robust concurrent test code.

Introduction

In unit testing, mocking method return values is a common requirement. When testing scenarios where multiple calls to the same method should return different results, Mockito provides several flexible solutions. This is particularly important when testing concurrent components like ExecutorCompletionService, where task completion order may be non-deterministic, but business logic must ensure consistent final outcomes.

Core Method: thenReturn Chaining

Mockito's thenReturn method supports chaining to specify different return values for consecutive method calls. The basic syntax is:

when(mockObject.methodName()).thenReturn(value1, value2, value3);

Alternatively, using explicit chaining:

when(mockObject.methodName()).thenReturn(value1).thenReturn(value2).thenReturn(value3);

With this configuration, the first call returns value1, the second returns value2, and the third and subsequent calls return value3. This approach is straightforward and suitable for scenarios with known, fixed return value sequences.

Advanced Method: thenAnswer Custom Logic

For more complex requirements, the thenAnswer method provides complete control. By implementing the Answer interface, you can dynamically determine return values based on call count, parameter values, or other conditions. Example code:

when(someMock.someMethod()).thenAnswer(new Answer<Object>() {
    private int count = 0;
    
    public Object answer(InvocationOnMock invocation) {
        if (count++ == 0)
            return "First Call";
        else if (count == 1)
            return "Second Call";
        else
            return "Subsequent Calls";
    }
});

Using Lambda expressions can further simplify the code:

when(someMock.someMethod()).thenAnswer(invocation -> {
    // Custom logic to return different values based on context
    return determineResponseBasedOnContext();
});

Practical Application: ExecutorCompletionService Testing Case

Consider the original problem's ExecutorCompletionService testing scenario. We need to mock the take().get() method to return different task results on multiple calls, verifying that business logic tolerates completion order variations. Implementation using thenAnswer:

ExecutorCompletionService<String> mockCompletionService = mock(ExecutorCompletionService.class);
Future<String> mockFuture = mock(Future.class);

when(mockCompletionService.take()).thenReturn(mockFuture);
when(mockFuture.get()).thenAnswer(new Answer<String>() {
    private int callCount = 0;
    
    public String answer(InvocationOnMock invocation) throws Throwable {
        switch (callCount++) {
            case 0: return "Result A";
            case 1: return "Result B";
            case 2: return "Result C";
            default: return "Default Result";
        }
    }
});

This approach ensures that tests cover all possible execution paths regardless of actual task completion order.

Method Comparison and Selection Guide

thenReturn Chaining is suitable for:

thenAnswer Custom Logic is suitable for:

Best Practices and Considerations

When using these techniques, consider the following:

  1. State Management: When using instance variables in thenAnswer to track call state, ensure thread safety.
  2. Exception Handling: Use thenThrow to simulate method exceptions, enhancing test coverage.
  3. Code Readability: Complex thenAnswer logic should be properly commented or extracted into separate methods.
  4. Performance Considerations: For high-frequency calls, thenReturn is generally more efficient than thenAnswer.

Conclusion

Mockito provides powerful tools to simulate methods returning different results on multiple calls. thenReturn chaining suits simple sequences, while thenAnswer offers unlimited flexibility. Proper use of these techniques significantly improves unit test quality and coverage, especially when testing concurrent and asynchronous code. Developers should choose the most appropriate method based on specific testing needs, balancing code simplicity and functional power.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.