Сравнить объект с свойствами datetime без микросекунд

Начиная с новых версий phpunit \ DateTime объекты сравниваются с точностью до микросекунд. Это не всегда хорошая идея, потому что, если у меня есть такой объект:

class QueueItem { public function __construct() { $this->setCreatedAt(new \DateTime('now', new \DateTimeZone('UTC'))); $this->setUpdatedAt(new \DateTime('now', new \DateTimeZone('UTC'))); } } 

Я никогда не смогу использовать assertEquals для всего объекта в своих тестах, потому что свойства datetime всегда будут отличаться из-за микросекунд.

Это не большое дело, когда просто сравнивать объекты (я могу сравнить каждое поле отдельно). Но здесь возникает проблема:

 $expectedQueue = [ new QueueItem(), new QueueItem() ]; $this->repositoryWrite ->expects($this->once()) ->method('save') ->with(...$expectedQueue); 

сравнивает весь массив аргументов, включая свойства datetime, которые я не могу предсказать.

Единственное, что у меня есть, это использование willReturnCallback и сравнение каждого аргумента отдельно:

  $this->repositoryWrite ->expects($this->once()) ->method('save') ->willReturnCallback(function(...$queue) use ($expectedQueue) { /** @var QueueItem[] $queue */ foreach ($queue as $k => $queueItem) { $this->assertEquals($expectedQueue->getSomeProp(), $queueItem[0]->getSomeProp()); } }); 

Это выглядит … странно. Есть ли другое решение для этого? Могу ли я изменить точность компаратора для объектов \ DateTime?

Время тестирования может быть довольно сложным – и, перейдя на стандартное разрешение по умолчанию в PHP7, оно стало более «интересным». В первом примере. это может быть не так сложно – установите $now = new \DateTime('now', new \DateTimeZone('UTC')); и установите поля, но это становится намного сложнее внутри класса, который вы тестируете, чтобы у вас не было такого простого контроля.

Однако с небольшим взломом PHP Namespace вы можете переопределить (на ограниченной основе) некоторые функции базового языка, такие как time() и sleep() . Компонент Symfony, « phpunit-bridge », предоставляет класс ClockMock, который может переопределить time() для управления часами – и превратить sleep(10) в вызов time()+10 – превращение паузы в реальном времени в тест почти нулевое время и точное сравнение.

Он может делать только так много и не может переопределить конструктор DateTime() , но его легко заменить тем, что будет использовать time() для его установки – $now = \DateTime::createFromFormat('U', (string) time()); и поэтому можно управлять с помощью ClockMocking.

Вы можете просто установить PHPUnit Bridge , а затем просто использовать класс ClockMock в своих тестах (с этим небольшим дополнительным использованием времени () для создания нового DateTime).

Я использую PHPUnit-мост в своих тестах, и чтобы включить Mocking Clock, я могу просто аннотировать тестовый класс с помощью «@group time-sensitive» (если он соответствует обычному типу присвоения имени классу, который он тестирует с помощью Test\ prefix и ...Test suffname), и мост узнает это и автоматически включит насмешку над классом – или я могу запустить его вручную в тестовом коде для других классов.