web-dev-qa-db-de.com

Wie man neues Date () in Java mit Mockito simuliert

Ich habe eine Funktion, die die aktuelle Zeit verwendet, um Berechnungen durchzuführen. Ich möchte es mit Mockito verspotten.

Ein Beispiel für die Klasse, die ich testen möchte:

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

Ich möchte etwas wie:

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

Kann man das verspotten? Ich möchte den "getesteten" Code nicht ändern, um getestet zu werden.

34
Jordi P.S.

Das Richtige ist, den Code umzustrukturieren, um ihn testfähiger zu machen (siehe unten). Durch Umstrukturieren des Codes zur Entfernung der direkten Abhängigkeit von Date können Sie verschiedene Implementierungen für normale Laufzeit und Testlaufzeit einfügen:

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);
    }
}
47
munyengm

Wenn Sie einen älteren Code haben, den Sie nicht umwandeln können und System.currentTimeMillis() nicht beeinflussen möchten, versuchen Sie dies mit Powermock und 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
  }
}
17
Daniel Mora

Sie könnten dies tun, indem Sie PowerMock verwenden, wodurch Mockito in der Lage ist, statische Methoden zu verspotten. Sie könnten dann mock System.currentTimeMillis() , woher new Date() letztendlich die Zeit bekommt.

Sie könnten . Ich werde keine Meinung vorbringen, ob Sie sollten .

6
Tom Anderson

Ein Ansatz, der die Frage nicht direkt beantwortet, aber möglicherweise das zugrunde liegende Problem lösen kann (mit reproduzierbaren Tests), besteht darin, die Variable Date als Parameter für Tests zuzulassen und einen Delegaten zum Standarddatum hinzuzufügen.

So

public class ClassToTest {

    public long getDoubleTime() {
      return getDoubleTime(new Date());
    }

    long getDoubleTime(Date date) {  // package visibility for tests
      return date.getTime() * 2;
    }
}

Im Produktionscode verwenden Sie getDoubleTime() und testen gegen getDoubleTime(Date date).

1
DerMike
Date now = new Date();    
now.set(2018, Calendar.FEBRUARY, 15, 1, 0); // set date to 2018-02-15
//set current time to 2018-02-15
mockCurrentTime(now.getTimeInMillis());

private void mockCurrentTime(long currTimeUTC) throws Exception {
    // mock new dates with current time
    PowerMockito.mockStatic(Date.class);
    PowerMockito.whenNew(Date.class).withNoArguments().thenAnswer(new Answer<Date>() {

        @Override
        public Date answer(InvocationOnMock invocation) throws Throwable {
            return new Date(currTimeUTC);
        }
    });

    //do not mock creation of specific dates
    PowerMockito.whenNew(Date.class).withArguments(anyLong()).thenAnswer(new Answer<Date>() {

        @Override
        public Date answer(InvocationOnMock invocation) throws Throwable {
            return new Date((long) invocation.getArguments()[0]);
        }
    });

    // mock new calendars created with time zone
    PowerMockito.mockStatic(Calendar.class);
    Mockito.when(Calendar.getInstance(any(TimeZone.class))).thenAnswer(new Answer<Calendar>() {
        @Override
        public Calendar answer(InvocationOnMock invocation) throws Throwable {
            TimeZone tz = invocation.getArgumentAt(0, TimeZone.class);
            Calendar cal = Calendar.getInstance(tz);
            cal.setTimeInMillis(currTimeUTC);
            return cal;
        }
    });
}
0
Avraham Shalev