How to mock new Date() in java using Mockito
Solution 1:
The right thing to do is to restructure your code to make it more testable as shown below. Restructuring your code to remove the direct dependency on Date will allow you to inject different implementations for normal runtime and test runtime:
interface DateTime {
Date getDate();
}
class DateTimeImpl implements DateTime {
@Override
public Date getDate() {
return new Date();
}
}
class MyClass {
private final DateTime dateTime;
// inject your Mock DateTime when testing other wise inject DateTimeImpl
public MyClass(final DateTime dateTime) {
this.dateTime = dateTime;
}
public long getDoubleTime(){
return dateTime.getDate().getTime()*2;
}
}
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);
}
}
Solution 2:
If you have legacy code that you cannot refactor and you do not want to affect System.currentTimeMillis()
, try this using Powermock
and PowerMockito
//note the static import
import static org.powermock.api.mockito.PowerMockito.whenNew;
@PrepareForTest({ LegacyClassA.class, LegacyClassB.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");
// everytime we call new Date() inside a method of any class
// declared in @PrepareForTest we will get the NOW instance
whenNew(Date.class).withNoArguments().thenReturn(NOW);
}
public class LegacyClassA {
public Date getSomeDate() {
return new Date(); //returns NOW
}
}
Solution 3:
You could do this by using PowerMock, which augments Mockito to be able to mock static methods. You could then mock System.currentTimeMillis()
, which is where new Date()
ultimately gets the time from.
You could. I'm not going to advance an opinion on whether you should.
Solution 4:
One approach, that does not directly answer the question but might solve the underlying problem (having reproducible tests), is allow the Date
as an parameter for tests and add a delegate to the default date.
Like so
public class ClassToTest {
public long getDoubleTime() {
return getDoubleTime(new Date());
}
long getDoubleTime(Date date) { // package visibility for tests
return date.getTime() * 2;
}
}
In production code, you use getDoubleTime()
and test against getDoubleTime(Date date)
.