Тестирование блоков управления с условием на текущее время

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

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

public boolean isValid()
{
    return isLicenseStringValid() && !isExpired();
}

public boolean isExpired()
{
    Date expiry = getExpiryDate();
    if( expiry == null ) {
        return false;
    }

    Date now = new Date();

    return now.after( expiry );
}

Итак, я не уверен, что делать, поскольку "новая функция Date()" не является статическим критерием.

  • Должен ли я не пытаться проверить 'isValid' и просто проверить 'isLicenseStringValid()' и функцию getExpiryDate() отдельно?
  • Я просто использую лицензионный ключ в тесте с сумасшедшим длительным сроком действия, чтобы у меня были переключения заданий к моменту истечения срока его действия?
  • Я пытаюсь высмеять "новую дату()" для некоторого метода getCurrentTime(), чтобы я мог подделать, в какое время это сейчас?

Что обычно делают другие с тестами, которые являются условными по времени?

Ответы

Ответ 1

Определенно макет new Date().

Создайте интерфейс Clock с помощью метода getCurrentTime() или чего-то подобного. Таким образом, вы можете иметь FakeClock для тестирования и SystemClock, который использует System.currentTimeMillis() или что-то еще.

Я делал это несколько раз - он работал очень хорошо. Это логично - эффективно вам требуется "служба текущего времени", поэтому ее следует вводить, как и любую другую зависимость.

Ответ 2

Я обычно вставляю поставщика даты в тестируемый код. Это также помогает, если вам нужно переключить соглашения или иначе "исправить" код тестирования времени.

Ответ 3

Используйте инъекцию зависимостей и введите TimeProvider, который предоставляет метод getExpiryDate().

Ответ 4

Если вы считаете, что абстракция TimeProvider/Clock слишком перегружена перфекционистом (что вполне может быть так), рассмотрите это вместо

Сделать getCurrentType защищенным виртуальным, а затем создать testingProductionType decendant из ProductType, который содержит отправленный вами код. В этом типе переопределите метод getCurrentType(), чтобы вернуть некоторый детерминированный результат. В unit test вместо этого создайте экземпляр этого TestingProductionType.

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

Ответ 5

Если вы можете проверить Mole на http://research.microsoft.com/en-us/projects/pex/ Moles allows to replace any .NET method with a delegate Просто используйте его, чтобы заменить Date и вернуть его, что вам нужно. Тогда вам не нужно ничего сумасшедшего.

-Raul

Ответ 6

Возможны все три подхода:

  • не проверяйте: ленивый путь человека
  • используйте лицензию, срок действия которой не истечет до тех пор, пока вы не оставите работу: закройте мой путь.
  • использовать макет для текущей даты, например, TimeProvider: перфекционистский способ

Я хотел бы включить: я бы добавил текущую дату в качестве параметра к методу isExpired и метод isValid. Для вашего живого производственного кода добавьте простое isValid() no-arg override, которое вызывает isValid(new Date()). В вашем тестовом коде используется версия, которая принимает текущую дату в качестве параметра.