it-roy-ru.com

Как смоделировать новый Date () в Java с помощью 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());
}

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

34
Jordi P.S.

Правильнее всего сделать так, чтобы реструктурировать ваш код, чтобы сделать его более тестируемым, как показано ниже Реструктуризация вашего кода для удаления прямой зависимости от Date позволит вам внедрить различные реализации для нормальной и тестовой среды выполнения:

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

Если у вас есть устаревший код, который вы не можете реорганизовать, и не хотите влиять на 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
  }
}
17
Daniel Mora

Вы могли бы сделать это, используя PowerMock , что расширяет возможности Mockito для имитации статических методов. Тогда вы могли бы mock System.currentTimeMillis() , откуда new Date() в конечном итоге получает время.

Вы могли бы. Я не собираюсь высказывать свое мнение о том, следует ли вам .

6
Tom Anderson

Один из подходов, который не дает прямого ответа на вопрос, но может решить основную проблему (имея воспроизводимые тесты), заключается в разрешении 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).

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