Keywords: Java | Unit Testing | Mockito | Dependency Injection | PowerMock
Abstract: This article explores two main approaches to mock new Date() in Java unit testing: refactoring code via dependency injection for better testability, and using PowerMock for legacy code. It details the best practice solution, including creating a DateTime interface, implementation class, and Mockito mocks, while introducing PowerMock as an alternative. By comparing both methods, it emphasizes the importance of designing for testability and provides complete code examples and testing scenarios.
Introduction
In Java unit testing, mocking time-related operations is a common challenge, especially when code directly uses new Date() to obtain the current time. This hard-coded dependency makes testing difficult because results vary over time. Based on the best answer from the Q&A data, this article discusses how to effectively mock new Date() using Mockito, with reference to other solutions as supplements.
Problem Analysis
Consider the following example class that uses new Date() to calculate a doubled timestamp:
public class ClassToTest {
public long getDoubleTime(){
return new Date().getTime()*2;
}
}In testing, we want to mock the getTime() method of Date to return a fixed value (e.g., 30) to verify that getDoubleTime() returns 60. However, directly mocking new Date() with Mockito is challenging because new Date() is a constructor call, not a method call.
Best Practice: Dependency Injection and Interface Design
According to the best answer, it is recommended to refactor the code by introducing dependency injection to improve testability. The specific steps are as follows:
- Define a Time Interface: Create a
DateTimeinterface to abstract time retrieval logic. - Implement the Interface: Provide a
DateTimeImplclass that returns the real date at runtime. - Modify the Class Under Test: Inject the
DateTimeinterface as a dependency intoMyClass. - Write Tests: Use Mockito to mock the
DateTimeandDateobjects.
Here is the implementation code:
interface DateTime {
Date getDate();
}
class DateTimeImpl implements DateTime {
@Override
public Date getDate() {
return new Date();
}
}
class MyClass {
private final DateTime dateTime;
public MyClass(final DateTime dateTime) {
this.dateTime = dateTime;
}
public long getDoubleTime(){
return dateTime.getDate().getTime()*2;
}
}In the test class, use Mockito to set up mock behavior:
public class MyClassTest {
private MyClass myClassTest;
@Before
public void setUp() {
final Date date = Mockito.mock(Date.class);
Mockito.when(date.getTime()).thenReturn(30L);
final DateTime dt = Mockito.mock(DateTime.class);
Mockito.when(dt.getDate()).thenReturn(date);
myClassTest = new MyClass(dt);
}
@Test
public void someTest() {
final long doubleTime = myClassTest.getDoubleTime();
assertEquals(60, doubleTime);
}
}This method decouples the time dependency through dependency injection, making tests more controllable and aligning with object-oriented design principles.
Alternative Approach: Using PowerMock for Legacy Code
For legacy code that cannot be refactored, refer to other answers using PowerMock. PowerMock extends Mockito to allow mocking of constructors, static methods, etc. Example code is as follows:
import static org.powermock.api.mockito.PowerMockito.whenNew;
@PrepareForTest({ LegacyClassA.class })
@Before
public void setUp() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("PST"));
Date NOW = sdf.parse("2015-05-23 00:00:00");
whenNew(Date.class).withNoArguments().thenReturn(NOW);
}
public class LegacyClassA {
public Date getSomeDate() {
return new Date(); // returns the mocked NOW instance
}
}This approach is suitable for emergency situations but may increase test complexity and has compatibility issues with some testing frameworks.
Comparison and Conclusion
Both methods have their pros and cons: the dependency injection solution enhances code testability and maintainability, making it the preferred choice for long-term projects; the PowerMock solution is applicable for handling legacy code but should be used cautiously to avoid over-reliance. In practice, it is recommended to prioritize dependency injection and consider PowerMock only when necessary.
Through the discussion in this article, readers can master the core techniques for mocking new Date(), improving the quality and efficiency of unit testing.