Я тестирую метод в своем классе, который выполняет некоторую проверку даты. Проблема в том, что этот метод зависит от сегодняшней даты (которая меняется каждый день), что затрудняет тестирование. Как я могу высмеять сегодняшнюю дату, чтобы мои тесты все равно проходили завтра?
PHPUnit: Как высмеивать сегодняшнюю дату, не передавая ее в качестве аргумента?
Ответ 1
Я ничего не знаю о PHP, но как в Java, так и в С# я бы передал "часы" некоторого описания - не сегодняшнюю дату, а объект, который вы можете запросить для текущей даты/времени. Затем в модульных тестах вы можете передать объект, который может дать любую дату, которая вам нужна, включая те, которые жестко закодированы в тестах.
Это тоже работает на PHP?
Ответ 2
Если ваш интерес заключается в том, чтобы не передавать дату, чтобы сохранить внешний интерфейс, тогда хороший способ сделать это - использовать "шов", чтобы указать дату:
class MyClass {
public function toBeTested() {
$theDate = $this->getDate();
...
}
protected function getDate() {
return date();
}
}
В общем случае этот класс работает нормально. Затем в тестировании вашего устройства вместо тестирования MyClass вы расширяете MyClass с помощью внутреннего класса, который переопределяет функцию getDate():
class MyTest extends phpunittestcase (sorry, writing this freeform, syntax is not exact!!) {
static $testDate;
public function testToBeTested() {
//set the date to be used
MyTest::testDate = '1/2/2000';
$classUnderTest = new MyClassWithDate();
$this->assertEquals('expected', $classUnderTest->toBeTested());
}
//just pass back the expected date
class MyClassWithDate extends MyClass {
protected function getDate() {
return MyTest::testDate;
}
}
}
В этом коде вы проверяете свое расширение реального класса, но ваше расширение переопределяет функцию шва (getDate()) и возвращает дату, которую вы хотите использовать для этого конкретного теста.
Опять же, извините, если есть какие-то вопиющие синтаксические ошибки, это было написано бесплатно.
Ответ 3
В то время как ответ Джона - это "правильный путь", другой вариант заключается в использовании расширения runkit extension для временной замены date()
и/или time()
с теми, которые возвращают фиксированное значение для теста.
- Обязательно установите
runkit.internal_override
вphp.ini
, чтобы вы могли переименовать встроенные функции. - Переименуйте исходную функцию с помощью
runkit_function_rename
. - Переименуйте функцию макета с исходным именем.
- Test.
- Переименуйте свой макет.
- Переименуйте оригинал назад.
Вот какой-то совершенно непроверенный код, который поможет с этим:
function mock_function($original, $mock) {
runkit_function_rename($original, $original . '_original');
runkit_function_rename($mock, $original);
}
function unmock_function($original, $mock) {
runkit_function_rename($original, $mock);
runkit_function_rename($original . '_original', $original);
}
Вы должны использовать их из методов setUp()
и tearDown()
, чтобы убедиться, что вы не мешаете другим последующим тестам.
Ответ 4
Я знаю, что вы не хотите передавать его в качестве аргумента. Но, может быть, вы можете переосмыслить это...
При передаче в качестве параметра извне, дата не является незначительной технической детализацией, а значительным функциональным правилом. Вам не нужно ни одно из следующего?
- Хотя текущая дата может быть обычным вариантом использования, правило может быть применимо к другой дате. Тогда ваш код будет более общим и работать в более позднем случае без изменений. Это происходит со мной регулярно...
- Несколько кодов могут использовать текущую дату в алгоритме. Поскольку скорость компьютера не бесконечна, у нескольких будет другой момент... Это логично функционально? Или будет использовать тот же самый момент (например, когда ваш пользователь нажал кнопку "Огонь" ), будет более точным? Подумайте, как вы могли бы запросить эти времена в своей базе данных позже, если они все разные в вашей базе данных, даже если они представляют один и тот же момент для вашего пользователя!
Ответ 5
Я не мог переименовать дважды, как говорит Дэвид, поэтому я получил его как:
function mockDate()
{
runkit_function_rename('date', 'test_date_override');
runkit_function_add('date','$format=NULL,$timestamp=NULL,$locale=NULL', 'return DATEMOCK;');
}
function unmockDate()
{
runkit_function_remove('date');
runkit_function_rename('test_date_override', 'date');
}