Насмешливое время в Java 8 Java для.время API


Джода время имеет хороший DateTimeUtils.setCurrentMillisFixed () издеваться над временем. Это очень практично в тестах. Есть ли эквивалент в в Java 8 Java для.время API?

6 59

6 ответов:

самое близкое-это

я использовал новый класс, чтобы скрыть Clock.fixed создание и упрощение тестов:

public class TimeMachine {

    private static Clock clock = Clock.systemDefaultZone();
    private static ZoneId zoneId = ZoneId.systemDefault();

    public static LocalDateTime now() {
        return LocalDateTime.now(getClock());
    }

    public static void useFixedClockAt(LocalDateTime date){
        clock = Clock.fixed(date.atZone(zoneId).toInstant(), zoneId);
    }

    public static void useSystemDefaultZoneClock(){
        clock = Clock.systemDefaultZone();
    }

    private static Clock getClock() {
        return clock ;
    }
}
public class MyClass {

    public void doSomethingWithTime() {
        LocalDateTime now = TimeMachine.now();
        ...
    }
}
@Test
public void test() {
    LocalDateTime twoWeeksAgo = LocalDateTime.now().minusWeeks(2);

    MyClass myClass = new MyClass();

    TimeMachine.useFixedClockAt(twoWeeksAgo);
    myClass.doSomethingWithTime();

    TimeMachine.useSystemDefaultZoneClock();
    myClass.doSomethingWithTime();

    ...
}

использование Clock загромождают ваш рабочий код.

можно использовать JMockit или PowerMock чтобы издеваться над вызовами статических методов в тестовом коде. Пример с JMockit:

@Test
public void testSth() {
  LocalDate today = LocalDate.of(2000, 6, 1);

  new Expectations(LocalDate.class) {{
      LocalDate.now(); result = today;
  }};

  Assert.assertEquals(LocalDate.now(), today);
}

EDIT: после прочтения комментариев к ответу Джона Скита на аналогичный вопрос вот так Я не согласен с моим прошлым. Больше всего аргумент убедил меня, что вы не можете распараллелить тесты когда вы издеваетесь над статическими методами.

вы можете / должны по-прежнему использовать статическое издевательство, если вам приходится иметь дело с устаревшим кодом.

я использовал поле

private Clock clock;

а то

LocalDate.now(clock);

в моем производстве код. Затем я использовал Mockito в своих модульных тестах, чтобы издеваться над часами с помощью часов.исправлено ():

@Mock
private Clock clock;
private Clock fixedClock;

насмешливый:

fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
doReturn(fixedClock.instant()).when(clock).instant();
doReturn(fixedClock.getZone()).when(clock).getZone();

утверждение:

assertThat(expectedLocalDateTime, is(LocalDate.now(fixedClock)));

вот рабочий способ переопределить текущее системное время на определенную дату для целей тестирования JUnit в веб-приложении Java 8 С помощью EasyMock

время Джоды, конечно, приятно (спасибо Стивен, Брайан, вы сделали наш мир лучше), но мне не разрешили его использовать.

после некоторых экспериментов я в конце концов придумал способ издеваться над временем до определенной даты в java 8-х java.время API с EasyMock

  • без времени Джоды API
  • и без PowerMock.

вот что нужно сделать:

что нужно сделать в тестируемом классе

Шаг 1

Добавить новый java.time.Clock атрибут тестируемого класса MyService и убедитесь, что новый атрибут будет правильно инициализирован по умолчанию с созданием блок или конструктор:

import java.time.Clock;
import java.time.LocalDateTime;

public class MyService {
  // (...)
  private Clock clock;
  public Clock getClock() { return clock; }
  public void setClock(Clock newClock) { clock = newClock; }

  public void initDefaultClock() {
    setClock(
      Clock.system(
        Clock.systemDefaultZone().getZone() 
        // You can just as well use
        // java.util.TimeZone.getDefault().toZoneId() instead
      )
    );
  }
  { initDefaultClock(); } // initialisation in an instantiation block, but 
                          // it can be done in a constructor just as well
  // (...)
}

Шаг 2

введите новый атрибут clock в метод что требует текущей даты-времени. Например, в моем случае мне пришлось выполнить проверку того, произошла ли дата, хранящаяся в dataase, раньше LocalDateTime.now(), который я переставил с LocalDateTime.now(clock), например:

import java.time.Clock;
import java.time.LocalDateTime;

public class MyService {
  // (...)
  protected void doExecute() {
    LocalDateTime dateToBeCompared = someLogic.whichReturns().aDate().fromDB();
    while (dateToBeCompared.isBefore(LocalDateTime.now(clock))) {
      someOtherLogic();
    }
  }
  // (...) 
}

что нужно сделать в тестовом классе

Шаг 3

в тестовом классе создайте макет объекта clock и введите его в экземпляр тестируемого класса непосредственно перед вызовом тестируемого метода doExecute(), а затем сбросить его обратно сразу после этого, как Итак:

import java.time.Clock;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import org.junit.Test;

public class MyServiceTest {
  // (...)
  private int year = 2017;  // Be this a specific 
  private int month = 2;    // date we need 
  private int day = 3;      // to simulate.

  @Test
  public void doExecuteTest() throws Exception {
    // (...) EasyMock stuff like mock(..), expect(..), replay(..) and whatnot

    MyService myService = new MyService();
    Clock mockClock =
      Clock.fixed(
        LocalDateTime.of(year, month, day, 0, 0).toInstant(OffsetDateTime.now().getOffset()),
        Clock.systemDefaultZone().getZone() // or java.util.TimeZone.getDefault().toZoneId()
      );
    myService.setClock(mockClock); // set it before calling the tested method

    myService.doExecute(); // calling tested method 

    myService.initDefaultClock(); // reset the clock to default right afterwards with our own previously created method

    // (...) remaining EasyMock stuff: verify(..) and assertEquals(..)
    }
  }

проверьте его в режиме отладки, и вы увидите, что дата 2017 Feb 3 была правильно введена в myService экземпляр и используется в инструкции сравнения, а затем был правильно сброшен на текущую дату с initDefaultClock().

этот пример даже показывает, как объединить мгновенное и локальное время (подробное объяснение проблем с преобразованием)

тестируемый класс

import java.time.Clock;
import java.time.LocalTime;

public class TimeMachine {

    private LocalTime from = LocalTime.MIDNIGHT;

    private LocalTime until = LocalTime.of(6, 0);

    private Clock clock = Clock.systemDefaultZone();

    public boolean isInInterval() {

        LocalTime now = LocalTime.now(clock);

        return now.isAfter(from) && now.isBefore(until);
    }

}

отличный тест

import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized

import java.time.Clock
import java.time.Instant

import static java.time.ZoneOffset.UTC
import static org.junit.runners.Parameterized.Parameters

@RunWith(Parameterized)
class TimeMachineTest {

    @Parameters(name = "{0} - {2}")
    static data() {
        [
            ["01:22:00", true,  "in interval"],
            ["23:59:59", false, "before"],
            ["06:01:00", false, "after"],
        ]*.toArray()
    }

    String time
    boolean expected

    TimeMachineTest(String time, boolean expected, String testName) {
        this.time = time
        this.expected = expected
    }

    @Test
    void test() {
        TimeMachine timeMachine = new TimeMachine()
        timeMachine.clock = Clock.fixed(Instant.parse("2010-01-01T${time}Z"), UTC)
        def result = timeMachine.isInInterval()
        assert result == expected
    }

}