Traditionally, mocking final classes and methods in Java was nearly impossible without tools like PowerMock. But with Mockito 2.x+, and improved in Mockito 3+, developers can now mock final classes and methods seamlessly using the inline mocking feature. This capability simplifies testing of legacy or sealed codebases where refactoring isn’t always an option.
Why Mock Final Classes and Methods?
- Legacy Libraries: Many frameworks use final classes for immutability or security.
- Third-Party APIs: Often delivered as final and cannot be subclassed.
- Bug Isolation: Focus on system behavior without invoking complex final implementations.
- Maintainable Tests: Improves CI/CD pipelines by preventing flaky interactions.
Analogy: Mocking final classes is like using a custom remote control override on a locked machine—you don’t open the internals, but you still simulate its behavior for testing.
Setup for Mocking Final Classes
To enable final mocking, you need the mockito-inline
dependency:
Maven
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
Gradle
testImplementation "org.mockito:mockito-inline:5.11.0"
Example: Mocking a Final Class
Final Class
public final class PaymentProcessor {
public String processPayment(double amount) {
return "Processed: $" + amount;
}
}
Service Using It
public class CheckoutService {
private final PaymentProcessor processor = new PaymentProcessor();
public String checkout(double amount) {
return processor.processPayment(amount);
}
}
Test with Mockito
@ExtendWith(MockitoExtension.class)
class CheckoutServiceTest {
@Test
void shouldMockFinalClass() {
PaymentProcessor mockProcessor = Mockito.mock(PaymentProcessor.class);
when(mockProcessor.processPayment(100.0)).thenReturn("Mocked Payment");
assertEquals("Mocked Payment", mockProcessor.processPayment(100.0));
}
}
Here, even though PaymentProcessor
is final, Mockito can mock it.
Mocking Final Methods
Mockito also allows mocking final methods within normal classes.
class ReportGenerator {
public final String generate() {
return "Real Report";
}
}
@Test
void shouldMockFinalMethod() {
ReportGenerator generator = Mockito.mock(ReportGenerator.class);
when(generator.generate()).thenReturn("Mocked Report");
assertEquals("Mocked Report", generator.generate());
}
Verifying Final Calls
Mockito lets you verify interactions on final methods:
@Test
void shouldVerifyFinalMethod() {
ReportGenerator generator = Mockito.mock(ReportGenerator.class);
generator.generate();
verify(generator).generate();
}
Best Practices
- Prefer Refactoring: Use dependency injection over final mocks if possible.
- Limit Final Mocking: Use it for legacy or third-party APIs, not your core domain code.
- Combine with Verifications: Ensure behavior correctness, not just return values.
- Isolate Side Effects: Avoid mocking where you lose test clarity.
- Scope Usage in CI/CD: Especially useful in pipelines where determinism is key.
Version Tracker
- Mockito 2.1.0: Introduced mocking of final classes and methods with inline mocking.
- Mockito 3+: Improved JUnit 5 compatibility and performance.
- Mockito 5+: Continuous improvements with Java 17/21 support.
Integration with Real-World Testing
In microservice environments:
- Use Mockito for final mocks to simulate third-party SDKs.
- Combine with Testcontainers for integration testing of databases and message queues.
- Use in CI/CD pipelines to ensure repeatable builds without relying on uncontrollable final implementations.
Conclusion & Key Takeaways
Mockito has eliminated the historical pain of dealing with final classes.
Key Takeaways:
- Add
mockito-inline
dependency for mocking final classes/methods. - Use sparingly—prefer clean architecture with interfaces.
- Excellent for legacy and third-party codebases.
- Combine with JUnit 5 and CI/CD practices for reliable pipelines.
FAQ
1. Can I mock final classes in Mockito?
Yes, with the mockito-inline
dependency.
2. Do I need special setup for mocking final methods?
Yes, add mockito-inline
in your test scope.
3. Can I mock final methods inside normal classes?
Yes, Mockito supports it.
4. Is mocking final classes safe?
Yes, but it should be scoped and used sparingly.
5. Should I refactor instead of mocking finals?
Refactoring is preferred, but mocking finals is practical for legacy code.
6. Can I verify interactions with final methods?
Yes, using verify()
.
7. Does this work with JUnit 5?
Yes, Mockito fully supports JUnit 5.
8. What about static final methods?
Static mocking is separate—use MockedStatic
.
9. Does mocking finals slow down tests?
Minimal impact; modern Mockito is efficient.
10. Do I need PowerMock for final classes?
No, modern Mockito eliminates that need.