Подтвердить что ты не робот

Как издеваться над новой датой() в 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());
}

Можно ли это издеваться над этим? Я бы не хотел менять "проверенный" код для тестирования.

4b9b3361

Ответ 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).