Как издеваться над новой датой() в java с помощью Mockito
У меня есть функция, которая использует текущее время для выполнения некоторых вычислений. Я хотел бы издеваться над этим, используя mockito.
Пример класса, который я хотел бы проверить:
public class ClassToTest {
public long getDoubleTime(){
return new Date().getTime()*2;
}
}
Мне хотелось бы что-то вроде:
@Test
public void testDoubleTime(){
mockDateSomeHow(Date.class).when(getTime()).return(30);
assertEquals(60,new ClassToTest().getDoubleTime());
}
Можно ли это издеваться над этим? Я бы не хотел менять "проверенный" код для тестирования.
Ответы
Ответ 1
Правильная вещь - это перестроить свой код, чтобы сделать его более проверяемым, как показано ниже.
Реструктуризация кода для удаления прямой зависимости от даты позволит вам внедрить различные реализации для нормальной среды выполнения и тестовой среды выполнения:
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);
}
}
Ответ 2
Если у вас есть устаревший код, который нельзя реорганизовать, и вы не хотите влиять на System.currentTimeMillis()
, попробуйте использовать это с помощью Powermock
и 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
}
}
Ответ 3
Вы можете сделать это, используя PowerMock, который увеличивает Mockito, чтобы мотать статические методы. Вы могли бы mock System.currentTimeMillis()
, где new Date()
в конечном итоге получает время.
Вы могли бы. Я не собираюсь выдвигать мнение о том, должны ли вы.
Ответ 4
Один подход, который напрямую не отвечает на вопрос, но может решить основную проблему (с воспроизводимыми тестами), позволяет Date
в качестве параметра для тестов и добавлять делегата к дате по умолчанию.
Таким образом
public class ClassToTest {
public long getDoubleTime() {
return getDoubleTime(new Date());
}
long getDoubleTime(Date date) { // package visibility for tests
return date.getTime() * 2;
}
}
В производственном коде вы используете getDoubleTime()
и проверяете на getDoubleTime(Date date)
.