Keywords: Mockito | Unit Testing | Java Testing Framework | Method Stubbing | Spy Objects
Abstract: This article provides an in-depth exploration of the fundamental differences between doReturn() and when() stubbing methods in the Mockito testing framework. Through detailed comparative analysis, it reveals the unique advantages of the doReturn/when syntax in spy object testing, void method stubbing, and repeated stubbing scenarios, offering complete code examples and best practice recommendations to help developers write more robust unit test code.
Introduction
In modern Java application development, unit testing is a critical component for ensuring code quality. Mockito, as one of the most popular Java testing frameworks, provides rich APIs for creating and managing test doubles. Among these, method stubbing is a core functionality of Mockito, allowing developers to define the behavior of mock objects during specific calls.
Basic Comparison of Two Stubbing Syntaxes
Mockito offers two primary stubbing syntaxes: when(...).thenReturn(...) and doReturn(...).when(...). While these two syntaxes appear to achieve the same functionality on the surface, deeper analysis reveals significant differences in their underlying implementation and usage scenarios.
Basic syntax examples:
// Syntax one: when/thenReturn
when(mockObject.someMethod()).thenReturn("value");
// Syntax two: doReturn/when
doReturn("value").when(mockObject).someMethod();
Core Advantages of doReturn/when Syntax
Critical Differences in Spy Object Scenarios
When using the @Spy annotation to create partial mock objects, the behavioral differences between the two syntaxes become particularly evident. The when/thenReturn syntax actually invokes the stubbed method, while doReturn/when completely avoids method invocation.
Consider the following example class:
public class ServiceClass {
protected String processData() {
return internalMethod();
}
protected String internalMethod() {
throw new IllegalStateException("Method should be mocked");
}
}
Test code comparison:
@Spy
private ServiceClass serviceSpy;
// Using doReturn/when - safe and effective
doReturn("mocked result").when(serviceSpy).internalMethod();
// Using when/thenReturn - throws IllegalStateException
when(serviceSpy.internalMethod()).thenReturn("mocked result");
The fundamental reason for this difference lies in the fact that when/thenReturn actually invokes the real method to obtain the call context during stubbing configuration, while doReturn/when directly manipulates Mockito's internal state without involving actual method invocation.
Stubbing Support for Void Methods
A significant advantage of the doReturn/when syntax is its ability to uniformly handle various types of stubbing scenarios, including exception throwing configuration for void methods:
// Configure void method to throw exception
doThrow(new RuntimeException()).when(mockObject).voidMethod();
// Configure void method to do nothing
doNothing().when(mockObject).voidMethod();
In contrast, the when/thenReturn syntax cannot be directly used for void method stubbing configuration, since void methods have no return value and cannot be chained within the when() call.
Flexibility in Repeated Stubbing and Complex Scenarios
In complex testing scenarios requiring multiple different stubbing configurations for the same method, the doReturn/when syntax demonstrates superior flexibility:
// Multiple stubbing for the same method
doReturn("first call").doReturn("second call").when(mockObject).someMethod();
// Or using clearer multiple calls
doReturn("first result").when(mockObject).someMethod();
doReturn("second result").when(mockObject).someMethod();
Type Checking Feature of when/thenReturn Syntax
While doReturn/when holds advantages in most scenarios, the when/thenReturn syntax provides a unique compile-time type checking feature. Since the when() method receives actual method calls, the compiler can verify type compatibility of return values:
// Compile-time type checking
when(mockObject.stringMethod()).thenReturn("valid"); // Correct
when(mockObject.stringMethod()).thenReturn(123); // Compilation error
However, the practical value of this type checking is limited, as type errors are quickly discovered during test runtime and do not substantially impact development efficiency.
Best Practice Recommendations
Based on in-depth analysis of both syntaxes, we recommend the following best practices:
- Unified Use of doReturn/when Syntax: To avoid cognitive load from switching between different syntaxes across scenarios, recommend unified use of
doReturn/whensyntax within projects. - Prefer doReturn/when for Spy Object Testing: When using the
@Spyannotation, always usedoReturn/whento avoid unnecessary actual method calls and potential exceptions. - Use Do Syntax for Void Method Stubbing: For exception throwing or behavior configuration of void methods, only use do-series methods like
doThrow,doNothing. - Team Coding Standard Consistency: In team projects, establish unified Mockito usage standards to ensure all members use the same stubbing syntax, improving code readability and maintainability.
Practical Application Case
Consider a Spring MVC controller testing scenario:
@Spy
private UserService userService;
@InjectMocks
private UserController userController;
@Test
public void testGetUserById() {
// Safely stub spy object method
doReturn(new User("John", "john@example.com"))
.when(userService)
.findUserById(1L);
// Execute test
ResponseEntity<User> response = userController.getUser(1L);
// Verify results
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().getName()).isEqualTo("John");
}
Conclusion
Through in-depth analysis of the two stubbing methods doReturn() and when() in Mockito, we can clearly recognize that while both syntaxes are functionally similar in simple mock scenarios, doReturn/when possesses irreplaceable advantages in spy object testing, void method stubbing, and complex stubbing scenarios. Unified use of the doReturn/when syntax not only simplifies the learning curve but also enhances the robustness and maintainability of test code. In practical project development, we recommend teams adopt unified do syntax standards to build more reliable and maintainable test suites.