J'ai une fonction qui utilise l'heure actuelle pour faire des calculs. Je voudrais me moquer de lui en utilisant mockito.
Un exemple de la classe que je voudrais tester:
public class ClassToTest {
public long getDoubleTime(){
return new Date().getTime()*2;
}
}
Je voudrais quelque chose comme:
@Test
public void testDoubleTime(){
mockDateSomeHow(Date.class).when(getTime()).return(30);
assertEquals(60,new ClassToTest().getDoubleTime());
}
Est-il possible de se moquer de cela? Je ne voudrais pas changer le code "testé" pour être testé.
La bonne chose à faire est de restructurer votre code pour le rendre plus testable, comme indiqué ci-dessous . Restructurer votre code pour supprimer la dépendance directe sur Date vous permettra d'injecter différentes implémentations pour l'exécution normale et l'exécution d'exécution:
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);
}
}
Si vous avez un code hérité que vous ne pouvez pas refactoriser et que vous ne voulez pas affecter à System.currentTimeMillis()
, essayez ceci en utilisant Powermock
et 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
}
}
Vous pouvez faire cela en utilisant PowerMock , ce qui augmente Mockito pour pouvoir se moquer des méthodes statiques. Vous pourriez alors simuler System.currentTimeMillis()
, qui est l'endroit où new Date()
obtient finalement l'heure.
Vous pourriez. Je ne vais pas vous faire un avis sur la question de savoir si vous devriez.
Une approche, qui ne répond pas directement à la question mais qui pourrait résoudre le problème sous-jacent (avoir des tests reproductibles), consiste à autoriser la variable Date
en tant que paramètre pour les tests et à ajouter un délégué à la date par défaut.
Ainsi
public class ClassToTest {
public long getDoubleTime() {
return getDoubleTime(new Date());
}
long getDoubleTime(Date date) { // package visibility for tests
return date.getTime() * 2;
}
}
Dans le code de production, vous utilisez getDoubleTime()
et testez-le contre getDoubleTime(Date date)
.
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;
}
});
}