In Mockito lenient() is used to silence exceptions thrown by strict stubbing. My understanding is that lenient() should generally be avoided, except perhaps as a temporary measure during Test-Driven Development (TDD). Strict stubbing exceptions typically indicate that there might be an issue with the code, the test design, or unnecessary lines in the test.
Are there any practical scenarios where using lenient() is actually necessary or beneficial for a test?
When writing tests in Test-Driven Development (TDD) where the implementation is still evolving, lenient() can help avoid exceptions from strict stubbing while the code is being developed.
This is especially useful when migrating from Mockito 1 to Mockito 2, as Mockito 2 introduced strict stubbing and lenient() can ease the transition during a time-sensitive migration.
In my experience, when working with legacy codebases that are being refactored or updated, lenient() can be a real lifesaver. It’s especially handy for managing strict stubbing issues temporarily while you clean up and reorganize the test suite.
While it’s not a permanent solution, I’ve found it useful for getting through those tricky refactors without constantly fighting strict rules, allowing you to focus on improving the tests step by step. It’s like giving yourself some breathing room to get things in order!
When using Mockito’s partial mocking features (e.g., stubbing some methods of a spy but not all), lenient()
can be used to avoid strict stubbing errors for methods that are not stubbed but are still part of the test setup.
In each of these cases, lenient()
can help bypass strict stubbing exceptions, but it should be used with caution and ideally removed once the code or test design is finalized.
In scenarios where you have shared setup methods for multiple tests, some mocks might be configured but not used in every single test.
Using lenient()
for these unused stubs prevents strict stubbing exceptions, allowing you to maintain a common setup without breaking individual tests that don’t exercise all the mocks.
To add to what @ian-partridge shared, when testing complex systems with dependencies that must exist for construction but aren’t invoked in the test scenario, lenient()
helps. It lets you provide stubs for these methods without causing strict stubbing failures, so your test can focus on the relevant behavior while ignoring uncalled setup methods.
Another practical use of lenient()
is when stubbing methods that are only occasionally called depending on runtime conditions, such as feature flags or optional flows. Without lenient()
, strict stubbing would fail when those methods are not invoked.
Example:
public class FeatureService {
public boolean isEnabled(String feature) {
// complex logic
return false;
}
}
@Test
void testOptionalFeatureFlow() {
FeatureService featureService = mock(FeatureService.class);
// Stub a method that might not be called in this test
lenient().when(featureService.isEnabled("newFeature")).thenReturn(true);
// Only the main flow is tested here
assertFalse(someOtherLogic(featureService));
}
Here, lenient()
prevents Mockito from throwing an exception because isEnabled("newFeature")
is never actually called in this test, letting the test focus on the relevant logic. This is especially helpful in large systems with optional behavior.