Временные модульные тесты

Мне нужно проверить функцию, результат которой будет зависеть от текущего времени (с использованием времени Joda isBeforeNow()).

    public boolean isAvailable() {
    return (this.someDate.isBeforeNow());
}

Можно ли заглушить/высмеять системное время с помощью Mockito, чтобы я мог надежно проверить функцию?

Ответ 2

Лучший способ (IMO) сделать ваш тестовый код - извлечь зависимость "то, что текущее время" в свой собственный интерфейс, с реализацией, которая использует текущее системное время (обычно используется) и реализацию, которая позволяет вам установите время, продвиньте его, как хотите, и т.д.

Я использовал этот подход в разных ситуациях, и он работал хорошо. Его легко настроить - просто создайте интерфейс (например, Clock), который имеет один метод, чтобы дать вам текущий момент в любом формате, который вы хотите (например, с помощью Joda Time или, возможно, Date).

Ответ 3

Java 8 представила абстрактный класс java.time.Clock, который позволяет вам иметь альтернативную реализацию для тестирования. Именно это и предложил Джон в своем ответе.

Ответ 4

Чтобы добавить к ответ Jon Skeet, Joda Time уже содержит интерфейс текущего времени: DateTimeUtils.MillisProvider

Например:

import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils.MillisProvider;

public class Check {
    private final MillisProvider millisProvider;
    private final DateTime someDate;

    public Check(MillisProvider millisProvider, DateTime someDate) {
        this.millisProvider = millisProvider;
        this.someDate = someDate;
    }

    public boolean isAvailable() {
        long now = millisProvider.getMillis();
        return (someDate.isBefore(now));
    }
}

Измените время в unit test (используя Mockito, но вы можете реализовать свой собственный класс MillisProviderMock):

DateTime fakeNow = new DateTime(2016, DateTimeConstants.MARCH, 28, 9, 10);
MillisProvider mockMillisProvider = mock(MillisProvider.class);
when(mockMillisProvider.getMillis()).thenReturn(fakeNow.getMillis());

Check check = new Check(mockMillisProvider, someDate);

Использовать текущее время в производстве (DateTimeUtils.SYSTEM_MILLIS_PROVIDER было добавлено к Joda Time в 2.9.3):

Check check = new Check(DateTimeUtils.SYSTEM_MILLIS_PROVIDER, someDate);

Ответ 5

Я использую подход, похожий на Jon's, но вместо создания специализированного интерфейса только для текущего времени (например, Clock) я обычно создаю специальный тестовый интерфейс (скажем, MockupFactory). Я поставил там все методы, которые мне нужны для проверки кода. Например, в одном из моих проектов у меня есть четыре метода:

  • тот, который возвращает клиента базы данных макета;
  • который создает объект "mock-up notifier", который уведомляет код об изменениях в базе данных;
  • который создает макет java.util.Timer, который запускает задачи, когда захочу;
  • который возвращает текущее время.

У тестируемого класса есть конструктор, который принимает этот интерфейс среди других аргументов. Тот, у кого нет этого аргумента, просто создает экземпляр по умолчанию этого интерфейса, который работает "в реальной жизни". И интерфейс, и конструктор являются закрытыми для пакета, поэтому API тестирования не течет вне пакета.

Если мне нужно больше имитируемых объектов, я просто добавляю метод к этому интерфейсу и реализую его как в тестовых, так и в реальных реализациях.

Таким образом, я разрабатываю код, подходящий для тестирования, в первую очередь, не накладывая слишком много на сам код. Фактически, код становится еще более чистым, так как код factory собирается в одном месте. Например, если мне нужно переключиться на другую реализацию клиента базы данных в реальном коде, мне нужно изменить только одну строку, а не искать вокруг ссылки на конструктор.

Конечно, как и в случае с Джоном, он не будет работать с сторонним кодом, который вы не можете или не можете изменить.