How to mock new Date() in java using Mockito

114,912

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).

Share:
114,912
Jordi P.S.
Author by

Jordi P.S.

Updated on November 29, 2021

Comments

  • Jordi P.S.
    Jordi P.S. over 2 years

    I have a function that uses the current time to make some calculations. I'd like to mock it using mockito.

    An example of the class I'd like to test:

    public class ClassToTest {
        public long getDoubleTime(){
            return new Date().getTime()*2;
        }
    }
    

    I'd like something like:

    @Test
    public void testDoubleTime(){
       mockDateSomeHow(Date.class).when(getTime()).return(30);
       assertEquals(60,new ClassToTest().getDoubleTime());
    }
    

    Is it possible to mock that? I wouldn't like to change the "tested" code in order to be tested.

  • stmax
    stmax almost 12 years
    I concur. I do it like that all the time. Works great, the change to the original code is minimal and testing it is easy.
  • Tom Anderson
    Tom Anderson almost 12 years
    This is the classic way to solve this problem (what you call DateTime might more descriptively be called Clock or something like that). However, this does mean restructuring your code and adding a little bit of complexity purely to allow testing, which is a bit of a code smell.
  • Jordi P.S.
    Jordi P.S. almost 12 years
    So I did, I think it's a good aproach, but the question was how to do it with Mockito :D
  • Jordi P.S.
    Jordi P.S. almost 12 years
    I'm interested in knowing how to do such a things, that's the aim of the question. Do you have examples ?
  • Brad
    Brad almost 12 years
    This is from the Powermock documentation. Should work the same with PowerMockito
  • Eugen Martynov
    Eugen Martynov over 11 years
    Just small question why you use Date class instead of System.currentTimeMillis()?
  • Jordi P.S.
    Jordi P.S. over 11 years
    The code there is was purely a sample, not prod code, but actually I'm using new Date(). Why? Cause I'm working with dates, not with Longs. I save a type conversion. My db stores dates (appengine datastore).
  • Brice
    Brice over 11 years
    Or even better : use JodaTime
  • Aneesh
    Aneesh over 9 years
    Blindly turning every "new" into a wrapper object and introducing an indirection through an injected dependency just makes the code un-necessarily verbose and hard. If you were creating an ArrayList or a HashMap inside your class would you now create an ArrayListFactory or a HashMapFactory and inject that into your class? Blindly using PowerMock everywhere you use "new" could create a very tightly coupled system as well. Being able to tell where tools like PowerMock are a good fit is part of being a skilled developer
  • Miyagi
    Miyagi over 5 years
    Or better yet use JMockit (which can do what Mockito and PowerMock can do together) so you don't need two different testing framworks...
  • Giszmo
    Giszmo almost 5 years
    Sad the answer doesn't answer the question. If my code code uses a library which uses a library which uses new Date() and my test stopped working because the test vector contained a certificate valid until yesterday, I don't see how to remove the use of new Date() from the library to quickly fix my test.
  • munyengm
    munyengm almost 5 years
    @Giszmo It answers the original question as posed by the OP. Sounds like you have a different question/requirement. Best post this as a new question.
  • Mz A
    Mz A almost 3 years
    When using new Date(), the code is inherently impure, and will only give the same result when running at a specific point in time, so I feel this solution makes the method "more" pure - and does not add too much complexity