Обработка юнит-тестов с условием на текущее время - PullRequest
15 голосов
/ 11 августа 2010

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

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

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 );
}

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

  1. Стоит ли мне не проверять isValid, а просто отдельно проверять isLicenseStringValid () и функцию getExpiryDate ()?
  2. Я просто использую лицензионный ключ в тесте с сумасшедшим длинным сроком действия, чтобы я сменил работу к тому времени, когда он истечет?
  3. Пытаюсь ли я смоделировать метод 'new Date ()' некоторым методом 'getCurrentTime ()', чтобы я мог фальсифицировать, который сейчас час?

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

Ответы [ 6 ]

24 голосов
/ 11 августа 2010

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

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

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

5 голосов
/ 11 августа 2010

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

2 голосов
/ 11 августа 2010

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

-Raul

2 голосов
/ 11 августа 2010

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

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

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

2 голосов
/ 11 августа 2010

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

1 голос
/ 11 августа 2010

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

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

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

...