ClockMock: a library to mock date and time in PHP

Posted on

If you write tests for your code (I hope you do), at some point you likely needed something to execute them with the system clock “frozen” to a specific date and time. Whoever had this need at least once knows that the matter is anything but trivial.

With this article, I want to tell the story of how we dealt with this problem at Slope. For doing this, I will use the following “codebase” (i.e. a pompous way to describe 2 classes — an entity and a service).


<?php
declare(strict_types=1);
namespace AppEntity;
use DateTimeImmutable;
class Entity
{
private DateTimeImmutable $creationDate;
private ?DateTimeImmutable $processingDate = null;
public function __construct()
{
$this->creationDate = new DateTimeImmutable();
}
public function getCreationDate(): DateTimeImmutable
{
return $this->creationDate;
}
public function getProcessingDate(): ?DateTimeImmutable
{
return $this->processingDate;
}
public function scheduleNextProcessing(DateTimeImmutable $processingDate): void
{
$this->processingDate = $processingDate;
}
}

view raw

Entity.php

hosted with ❤ by GitHub


<?php
declare(strict_types=1);
namespace AppService;
use DateTimeImmutable;
use Exception;
use AppEntityEntity;
class Service
{
public function doSomething(): void
{
$now = new DateTimeImmutable();
if ((int) $now->format(‘d’) > 28) {
throw new Exception(‘This thing cannot be done after the 28th of every month.’);
}
// … Something is done here
}
public function scheduleProcessing(Entity $entity): void
{
$entity->scheduleNextProcessing(new DateTimeImmutable(‘+5 minutes’));
}
}

view raw

Service.php

hosted with ❤ by GitHub

Our example codebase, using just vanilla PHP.

I will describe 3 testing scenarios (a, b. and c.) that pretty much cover 100% of our cases/needs. Your mileage may vary, but the same concepts should apply.

Our journey starts from a situation in which we simply accepted to test the following scenarios in a sub-optimal way — or to not test them at all.



Scenario a: alter the creation date of an entity when preparing fixtures for integration tests

Our “basic” go-to solution here was reflection, used to artificially modify the date (stored in a private instance property) right after calling the entity constructor. Example:


Leave a Reply

Your email address will not be published.

<?php
declare(strict_types=1);
use DateTimeImmutable;
use ReflectionClass;
use PHPUnitFrameworkTestCase;
use AppEntitiesEntity;
class SomeIntegrationTest extends TestCase
{
public function test_something_related_to_entity_creation_date()