Зависящие от времени юнит-тесты

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

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

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

5 ответов

Решение

Joda Time поддерживает установку "фальшивого" текущего времени через setCurrentMillisFixed а также setCurrentMillisOffset методы DateTimeUtils учебный класс.

См. http://joda-time.sourceforge.net/api-release/org/joda/time/DateTimeUtils.html

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

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

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

Чтобы добавить ответ Джона Скита, 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));
    }
}

Смоделируйте время в модульном тесте (используя 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);

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

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

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

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

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

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

Другие вопросы по тегам