Поскольку я использую Symfony WebTestCase для выполнения функционального тестирования с использованием пакета тестирования PHPUnit, быстро стало нецелесообразно издеваться над всеми использованиями класса DateTime.
Я хотел протестировать приложение, так как оно обрабатывает запросы с течением времени, такие как тестирование файлов cookie или истечение срока действия кэша и т. Д.
Лучший способ, который я нашел для этого, - это реализовать мой собственный класс DateTime, который расширяет класс по умолчанию и предоставляет некоторые статические методы, позволяющие добавлять / вычитать время по умолчанию для всех объектов DateTime, создаваемых из эта точка вперед.
Это действительно простая в использовании функция, которая не требует установки пользовательских библиотек.
caveat emptor: Единственным недостатком этого метода является то, что среда Symfony (или какая-либо другая среда, которую вы используете) не будет использовать вашу библиотеку, поэтому любое поведение, которое она должна обрабатывать, например истечение срока действия кэша / файла cookie, эти изменения не будут затронуты.
namespace My\AppBundle\Util;
/**
* Class DateTime
*
* Allows unit-testing of DateTime dependent functions
*/
class DateTime extends \DateTime
{
/** @var \DateInterval|null */
private static $defaultTimeOffset;
public function __construct($time = 'now', \DateTimeZone $timezone = null)
{
parent::__construct($time, $timezone);
if (self::$defaultTimeOffset && $this->isRelativeTime($time)) {
$this->modify(self::$defaultTimeOffset);
}
}
/**
* Determines whether to apply the default time offset
*
* @param string $time
* @return bool
*/
public function isRelativeTime($time)
{
if($time === 'now') {
//important, otherwise we get infinite recursion
return true;
}
$base = new \DateTime('2000-01-01T01:01:01+00:00');
$base->modify($time);
$test = new \DateTime('2001-01-01T01:01:01+00:00');
$test->modify($time);
return ($base->format('c') !== $test->format('c'));
}
/**
* Apply a time modification to all future calls to create a DateTime instance relative to the current time
* This method does not have any effect on existing DateTime objects already created.
*
* @param string $modify
*/
public static function setDefaultTimeOffset($modify)
{
self::$defaultTimeOffset = $modify ?: null;
}
/**
* @return int the unix timestamp, number of seconds since the Epoch (Jan 1st 1970, 00:00:00)
*/
public static function getUnixTime()
{
return (int)(new self)->format('U');
}
}
Использовать это просто:
public class myTestClass() {
public function testMockingDateTimeObject()
{
echo "fixed: ". (new DateTime('18th June 2016'))->format('c') . "\n";
echo "before: ". (new DateTime('tomorrow'))->format('c') . "\n";
echo "before: ". (new DateTime())->format('c') . "\n";
DateTime::setDefaultTimeOffset('+25 hours');
echo "fixed: ". (new DateTime('18th June 2016'))->format('c') . "\n";
echo "after: ". (new DateTime('tomorrow'))->format('c') . "\n";
echo "after: ". (new DateTime())->format('c') . "\n";
// fixed: 2016-06-18T00:00:00+00:00 <-- stayed same
// before: 2016-09-20T00:00:00+00:00
// before: 2016-09-19T11:59:17+00:00
// fixed: 2016-06-18T00:00:00+00:00 <-- stayed same
// after: 2016-09-21T01:00:00+00:00 <-- added 25 hours
// after: 2016-09-20T12:59:17+00:00 <-- added 25 hours
}
}