Keywords: Mockito | Unit Testing | Strict Stubbing | JUnit 5 | Test Mocking
Abstract: This article provides an in-depth analysis of the strict stubbing mechanism introduced in Mockito 2.x and its behavioral changes in JUnit 5 environments. Through examination of a typical stubbing argument mismatch error case, the article explains the differences and application scenarios among three strictness levels: STRICT_STUBS, WARN, and LENIENT. It focuses on best practices using the lenient() method for localized stubbing relaxation, while comparing alternative approaches using Answer interface and global MockitoSettings annotation. The article also discusses how strict stubbing improves test code quality and offers practical guidance for migrating from Mockito 1.x to 2.x.
Core Changes in Mockito 2.x Strict Stubbing Mechanism
Mockito 2.x introduced a strict stubbing verification mechanism, which represents the most significant difference from Mockito 1.x. In JUnit 5 environments, when using the @ExtendWith(MockitoExtension.class) annotation, the default strictness level is Strictness.STRICT_STUBS. This change aims to improve test code quality and maintainability, but also brings compatibility issues during migration.
Analysis of Strict Stubbing Error Cases
Consider the following test code example that worked correctly in Mockito 1.x but throws a PotentialStubbingProblem exception in Mockito 2.x:
@ExtendWith(MockitoExtension.class)
public class MockitoExample {
static abstract class TestClass {
public abstract int booleanMethod(boolean arg);
}
@Mock
TestClass testClass;
@BeforeEach
public void beforeEach() {
when(testClass.booleanMethod(eq(true))).thenReturn(1);
when(testClass.booleanMethod(eq(false))).thenReturn(2);
}
@Test
public void test() {
assertEquals(1,testClass.booleanMethod(true));
assertEquals(2,testClass.booleanMethod(false));
}
}The error message shows: "Strict stubbing argument mismatch. Please check...". The root cause is that under the STRICT_STUBS strictness level, multiple stubbing calls to the same method are considered potential error patterns, even when these stubs use different arguments.
Solution 1: Using lenient() for Localized Relaxation
Starting from Mockito 2.20, the lenient() method can be used to relax specific stubbing calls. This represents the best practice for solving the aforementioned problem:
@BeforeEach
public void beforeEach() {
lenient().when(testClass.booleanMethod(eq(true))).thenReturn(1);
lenient().when(testClass.booleanMethod(eq(false))).thenReturn(2);
}The lenient() method allows multiple stubbings on the same method of the same mock object within the same test method without triggering strict verification. This approach maintains test rigor while providing necessary flexibility.
Solution 2: Using Answer Interface for Unified Handling
Another solution involves using Mockito's Answer interface to handle all parameter cases through a single stubbing call:
@BeforeEach
public void beforeEach() {
when(testClass.booleanMethod(anyBoolean())).thenAnswer(invocationOnMock -> {
if ((boolean) invocationOnMock.getArguments()[0]) {
return 1;
}
return 2;
});
}This approach avoids multiple stubbing calls and fully complies with STRICT_STUBS requirements, though it may complicate test logic.
Solution 3: Adjusting Global Strictness Level
The strictness level can be adjusted for the entire test class using the @MockitoSettings annotation:
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class MockitoExample {
// Test code
}Or for specific test methods:
@MockitoSettings(strictness = Strictness.WARN)
@Test
void specificTest() {
// Test code
}Mockito provides three strictness levels: LENIENT (minimum strictness, default in Mockito 1.x), WARN (provides console warnings, default in Mockito 2.x with JUnit 4), and STRICT_STUBS (maximum strictness, default in Mockito 2.x with JUnit 5).
Advantages and Considerations of Strict Stubbing
The main advantages of strict stubbing include: improved test code clarity, reduced duplicate stubbing code, and enhanced debugging capabilities. However, in certain edge cases, such as when tests need to verify method behavior with different arguments, false positives may occur.
Mockito documentation explicitly states that strict verification may produce false negatives in the following legitimate scenarios:
- Multiple stubbings on the same method using
when().then()API - Code under test intentionally invokes stubbed methods with different arguments
For these cases, using lenient(), doReturn().when() API, or adjusting strictness levels is recommended.
Migration Recommendations and Best Practices
When migrating from Mockito 1.x to 2.x, the following steps are recommended:
- First set strictness level to
WARNand observe console output - Gradually fix all warnings to ensure correct test logic
- Use
lenient()annotation for legitimate scenarios requiring multiple stubbings - Consider refactoring complex stubbing logic using
Answerinterface - The ultimate goal is to elevate strictness level to
STRICT_STUBSfor optimal test quality
By properly utilizing Mockito 2.x's strict stubbing mechanism, unit test reliability and maintainability can be significantly improved, providing a solid foundation for continuous integration and delivery.