Skip to content

Usage with Timestampable / MockClock #947

Open
@treztreiz

Description

@treztreiz

Hello 😊

I'm encountering an issue when trying to use the clock component to mock dates created at flush time using timestampable with StofDoctrineExtensionsBundle.

The entity and factory used for this example:

class MyEntity
{
    #[ORM\Column]
    #[Gedmo\Timestampable(on: 'create')]
    protected ?\DateTimeImmutable $createdAt = null;

    public function getCreatedAt(): ?\DateTimeImmutable
    {
        return $this->createdAt;
    }

    public function setCreatedAt(\DateTimeImmutable $createdAt): static
    {
        $this->createdAt = $createdAt;

        return $this;
    }
}
final class MyEntityFactory extends PersistentProxyObjectFactory
{
    public static function class(): string
    {
        return MyEntity::class;
    }

    protected function defaults(): array|callable
    {
        return [];
    }
}

The Problem

When flushing with the entity manager, the doctrine listener correctly uses MockClock as the ClockInterface. The time is frozen and makes dates comparison predictable:

class ClockTest extends KernelTestCase
{
    use ResetDatabase, Factories, ClockSensitiveTrait;

    public function testMockClockIsUsedAsClockInterface(): void
    {
        $clock = static::mockTime();

        $entity = new MyEntity();
        $em = static::getContainer()->get(EntityManagerInterface::class);
        $em->persist($entity);
        $em->flush();

        self::assertEquals($clock->now(), $entity->getCreatedAt()); 

        // assertion successful
        // Expected : 2025-07-14T10:41:49.282334+0000
        // Actual   : 2025-07-14T10:41:49.282334+0000
    }
}

When creating the entity with Foundry, the doctrine listener seems to not use MockClock. The generated time still uses the default timezone and does not have milliseconds for a reason I don't understand:

class ClockTest extends KernelTestCase
{
    use ResetDatabase, Factories, ClockSensitiveTrait;

    public function testMockClockIsUsedAsClockInterface(): void
    {
        $clock = static::mockTime();

        $entity = MyEntityFactory::createOne();

        self::assertEquals($clock->now(), $entity->getCreatedAt());

        // assertion fails
        // Expected : 2025-07-14T10:41:49.282334+0000
        // Actual   : 2025-07-14T10:41:49.000000+0200
    }
}

I may be missing some knowledge to solve this use case, can someone point me in some direction ?
Thank you !

Version

php: 8.4.10
symfony: 7.3.1

// Doctrine and test related libraries
brianium/paratest: 7.10.3
dama/doctrine-test-bundle: 8.3.0
doctrine/dbal: 3.10.0
doctrine/doctrine-bundle: 2.15.0 
doctrine/doctrine-fixtures-bundle: 4.1.0
doctrine/doctrine-migrations-bundle: 3.4.2
doctrine/orm: 3.5.0
phpunit/phpunit: 12.2.7
stof/doctrine-extensions-bundle: 1.14.0
zenstruck/foundry: 2.6.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions