In modern Java development, constructor mocking is generally discouraged in favor of dependency injection. However, in legacy codebases, developers often encounter classes that instantiate dependencies directly inside their constructors, making them hard to test with standard Mockito.
That’s where PowerMock comes in. PowerMock extends Mockito to mock constructors, static methods, and final classes, making it useful for maintaining legacy projects until they are refactored.
This tutorial explores how to use PowerMock with Mockito to mock constructors effectively.
Why Constructor Mocking Matters
- Legacy systems: Older applications often instantiate dependencies directly in constructors.
- Hard-to-refactor code: Sometimes, immediate refactoring is not possible due to time constraints.
- Unit testing needs: Without constructor mocking, you may be forced into integration testing rather than focused unit tests.
Best Practice Note: While constructor mocking can unblock testing, strive to refactor toward dependency injection for long-term maintainability.
Setting up PowerMock with Mockito
First, add dependencies in your Maven pom.xml:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
Example: Mocking a Constructor
Suppose we have the following service:
public class UserService {
private final DatabaseConnection db;
public UserService() {
this.db = new DatabaseConnection(); // hard dependency
}
public String getUserName(int id) {
return db.findUserNameById(id);
}
}
public class DatabaseConnection {
public String findUserNameById(int id) {
// Imagine real DB call
return "RealUser";
}
}
Test with PowerMock
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserServiceTest {
@Test
public void testGetUserNameWithMockedConstructor() throws Exception {
DatabaseConnection mockDb = Mockito.mock(DatabaseConnection.class);
Mockito.when(mockDb.findUserNameById(1)).thenReturn("MockedUser");
PowerMockito.whenNew(DatabaseConnection.class)
.withNoArguments()
.thenReturn(mockDb);
UserService service = new UserService();
String result = service.getUserName(1);
Assertions.assertEquals("MockedUser", result);
}
}
Real-World Scenarios
- Legacy frameworks where dependency injection was not used.
- Testing third-party libraries that create internal objects.
- Maintaining stability during a phased refactor.
Best Practices
- Avoid constructor mocking in new codebases.
- Use PowerMock as a temporary bridge while refactoring to dependency injection.
- Document where and why PowerMock is used.
- Limit PowerMock usage because it can cause compatibility issues with JUnit 5 and new JVM versions.
Modern Alternatives
Instead of constructor mocking, prefer:
- Dependency Injection (DI) frameworks like Spring.
- Factories or builders that externalize object creation.
- Refactoring legacy code to pass dependencies.
Version Tracker
- Mockito (Pre-2.x) could not mock constructors.
- PowerMock became the workaround.
- Mockito 3+ and Java 11+ encourage avoiding PowerMock in favor of DI.
Conclusion
Constructor mocking with PowerMock is a legacy technique that helps unblock testing in difficult scenarios. While still useful in maintaining old projects, modern practices recommend dependency injection and cleaner design to avoid such workarounds.
FAQ
1. Why can’t Mockito mock constructors directly?
Mockito follows the principle of testing by contract and encourages dependency injection, not constructor interception.
2. Is PowerMock still maintained?
It is in maintenance mode, and compatibility issues arise with JUnit 5. Use cautiously.
3. Can I use PowerMock with JUnit 5?
Limited support exists, but JUnit 4 remains the primary integration.
4. What are alternatives to constructor mocking?
Dependency Injection, refactoring, or Testcontainers for integration testing.
5. Is constructor mocking good for TDD?
No. TDD emphasizes clean design; constructor mocking indicates design issues.
6. What if my company codebase heavily uses new
operators?
Introduce factories incrementally and phase out direct constructor usage.
7. Does PowerMock slow down tests?
Yes, it uses bytecode manipulation, which can reduce performance.
8. Should I combine PowerMock with Testcontainers?
Only if integration testing is needed; otherwise, stick to mocks.
9. Is PowerMock safe with modern JVMs?
It works but may break in future versions; proceed with caution.
10. When should I completely avoid PowerMock?
In greenfield projects or when you can refactor toward dependency injection.