Ответ 1
Joda time поддерживает установку "поддельного" текущего времени с помощью методов setCurrentMillisFixed
и setCurrentMillisOffset
класса DateTimeUtils
.
См. Https://www.joda.org/joda-time/apidocs/org/joda/time/DateTimeUtils.html.
Мне нужно проверить функцию, результат которой будет зависеть от текущего времени (с использованием времени Joda isBeforeNow()
).
public boolean isAvailable() {
return (this.someDate.isBeforeNow());
}
Можно ли заглушить/высмеять системное время с помощью Mockito, чтобы я мог надежно проверить функцию?
Joda time поддерживает установку "поддельного" текущего времени с помощью методов setCurrentMillisFixed
и setCurrentMillisOffset
класса DateTimeUtils
.
См. Https://www.joda.org/joda-time/apidocs/org/joda/time/DateTimeUtils.html.
Лучший способ (IMO) сделать ваш тестовый код - извлечь зависимость "то, что текущее время" в свой собственный интерфейс, с реализацией, которая использует текущее системное время (обычно используется) и реализацию, которая позволяет вам установите время, продвиньте его, как хотите, и т.д.
Я использовал этот подход в разных ситуациях, и он работал хорошо. Его легко настроить - просто создайте интерфейс (например, Clock
), который имеет один метод, чтобы дать вам текущий момент в любом формате, который вы хотите (например, с помощью Joda Time или, возможно, Date
).
Java 8 представила абстрактный класс java.time.Clock
, который позволяет вам иметь альтернативную реализацию для тестирования. Именно это и предложил Джон в своем ответе.
Чтобы добавить к ответ 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);
Я использую подход, похожий на Jon's, но вместо создания специализированного интерфейса только для текущего времени (например, Clock
) я обычно создаю специальный тестовый интерфейс (скажем, MockupFactory
). Я поставил там все методы, которые мне нужны для проверки кода. Например, в одном из моих проектов у меня есть четыре метода:
У тестируемого класса есть конструктор, который принимает этот интерфейс среди других аргументов. Тот, у кого нет этого аргумента, просто создает экземпляр по умолчанию этого интерфейса, который работает "в реальной жизни". И интерфейс, и конструктор являются закрытыми для пакета, поэтому API тестирования не течет вне пакета.
Если мне нужно больше имитируемых объектов, я просто добавляю метод к этому интерфейсу и реализую его как в тестовых, так и в реальных реализациях.
Таким образом, я разрабатываю код, подходящий для тестирования, в первую очередь, не накладывая слишком много на сам код. Фактически, код становится еще более чистым, так как код factory собирается в одном месте. Например, если мне нужно переключиться на другую реализацию клиента базы данных в реальном коде, мне нужно изменить только одну строку, а не искать вокруг ссылки на конструктор.
Конечно, как и в случае с Джоном, он не будет работать с сторонним кодом, который вы не можете или не можете изменить.