Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Uid] AddMockUuidFactory for deterministic UUID generation in tests#61807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
nicolas-grekas merged 1 commit intosymfony:7.4frommomito69:mock-uuid-factory
Sep 25, 2025

Conversation

@momito69
Copy link
Contributor

QA
Branch?7.4
Bug fix?no
New feature?yes
Deprecations?no
IssuesFix#60978
LicenseMIT

Similar to the Clock component's ClockInterface and MockClock, I've made a MockUuidFactory and implemented an UuidFactoryInterface to have a way to mock UUID generation for testing purposes.

cc@OskarStark ,@alexandre-daubois

Copy link
Member

@nicolas-grekasnicolas-grekas left a comment
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think we shouldn't extend the current API of factories. Let's inherit from UuidFactory instead, and let's remove all the added methods, they're breaking the design of the factory API: v3+5 don't make sense mocking, v1/6/7 are all time-based and the abstraction can be kept, v4 is already what therandomBased method returns so we don't need new methods, etc.

momito69 reacted with thumbs up emoji
@symfonysymfony deleted a comment fromcarsonbotSep 23, 2025
@momito69momito69force-pushed themock-uuid-factory branch 7 times, most recently from39e3304 to56eae5dCompareSeptember 23, 2025 13:55
Copy link
Member

@nicolas-grekasnicolas-grekas left a comment
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Instead of a review, I thought it would be quicker to write the factory, so here is how I see this could be. If you could give it a try and add tests that'd be awesome!

<?php/* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */namespaceSymfony\Component\Uid\Factory;useSymfony\Component\Uid\TimeBasedUidInterface;useSymfony\Component\Uid\Uuid;useSymfony\Component\Uid\UuidV3;useSymfony\Component\Uid\UuidV4;useSymfony\Component\Uid\UuidV5;useSymfony\Component\Uid\UuidV7;class MockUuidFactoryextends UuidFactory{private\Iterator$sequence;/**     * @param iterable<string|Uuid> $uuids     */publicfunction__construct(iterable$uuids,privateUuid|string|null$timeBasedNode =null,privateUuid|string|null$nameBasedNamespace =null,    ) {$this->sequence =match (true) {\is_array($uuids) =>new \ArrayIterator($uuids),$uuidsinstanceof \Iterator =>$uuids,$uuidsinstanceof \Traversable =>new \IteratorIterator($uuids),        };    }publicfunctioncreate():Uuid    {if (!$this->sequence->valid()) {thrownew \RuntimeException('No more UUIDs in sequence.');        }$uuid =$this->sequence->current();$this->sequence->next();return$uuidinstanceof Uuid ?$uuid : Uuid::fromString($uuid);    }publicfunctionrandomBased():RandomBasedUuidFactory    {if (!($uuid =$this->create())instanceof UuidV4) {thrownew \RuntimeException(\sprintf('Next UUID in sequence is not a UuidV4: "%s".',get_debug_type($uuid)));        }returnnewclass($uuid)extends RandomBasedUuidFactory {publicfunction__construct(privateUuidV4$uuid,            ) {            }publicfunctioncreate():UuidV4            {return$this->uuid;            }        };    }publicfunctiontimeBased(Uuid|string|null$node =null):TimeBasedUuidFactory    {if (!($uuid =$this->create())instanceof TimeBasedUidInterface) {thrownew \RuntimeException(\sprintf('Next UUID in sequence is not a TimeBasedUidInterface: "%s".',get_debug_type($uuid)));        }if (\is_string($node ??=$this->timeBasedNode)) {$node = Uuid::fromString($node);        }returnnewclass($uuid,$node)extends TimeBasedUuidFactory {publicfunction__construct(privateTimeBasedUidInterface$uuid,private ?Uuid$node =null,            ) {if ($uuidinstanceof UuidV7) {$this->node =null;                }            }publicfunctioncreate(?\DateTimeInterface$time =null):Uuid&TimeBasedUidInterface            {if (null !==$time &&$this->uuid->getDateTime() !=$time) {thrownew \RuntimeException(\sprintf('Next UUID in sequence does not match the expected time: "%s" != "%s".',$this->uuid->getDateTime()->format('@U.uT'),$time->format('@U.uT')));                }if (null !==$this->node &&$this->uuid->getNode() !==substr($this->node->toRfc4122(), -12)) {thrownew \RuntimeException(\sprintf('Next UUID in sequence does not match the expected node: "%s" != "%s".',$this->uuid->getNode(),substr($this->node->toRfc4122(), -12)));                }return$this->uuid;            }        };    }publicfunctionnameBased(Uuid|string|null$namespace =null):NameBasedUuidFactory    {if (!($uuid =$this->create())instanceof UuidV5 && !$uuidinstanceof UuidV3) {thrownew \RuntimeException(\sprintf('Next UUID in sequence is not a UuidV5 or UuidV3: "%s".',get_debug_type($uuid)));        }$factory =parent::nameBased($namespace);returnnewclass ($uuid,$factory)extends NameBasedUuidFactory {publicfunction__construct(privateUuidV5|UuidV3$uuid,privateNameBasedUuidFactory$factory,            ) {            }publicfunctioncreate(string$name):UuidV5|UuidV3            {if ($this->uuid->toRfc4122() !==$this->factory->create($name)->toRfc4122()) {thrownew \RuntimeException(\sprintf('Next UUID in sequence does not match the expected named UUID: "%s" != "%s".',$this->uuid->toRfc4122(),$this->factory->create($name)->toRfc4122()));                }return$this->uuid;            }        };    }}

@nicolas-grekas
Copy link
Member

Ah, I can already tell you I made a mistake: the sequence should be consumed in child factories, not in the main one !

@momito69
Copy link
ContributorAuthor

@nicolas-grekas looks good I'll try you suggestion. And about the sequence I'll take care of it.

@momito69momito69 marked this pull request as draftSeptember 23, 2025 16:54
@nicolas-grekasnicolas-grekas marked this pull request as ready for reviewSeptember 24, 2025 09:01
Copy link
Member

@nicolas-grekasnicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

thanks for the new tests!
here are some more tweaks for the code and then we should be good to go!

momito69 reacted with thumbs up emoji
@OskarStarkOskarStark changed the title[Uid] Add MockUuidFactory for deterministic UUID generation in tests[Uid] AddMockUuidFactory for deterministic UUID generation in testsSep 25, 2025
Copy link
Contributor

@OskarStarkOskarStark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Many thanks 🚀

momito69 reacted with heart emoji
@nicolas-grekas
Copy link
Member

Thank you@momito69.

momito69 reacted with heart emoji

@nicolas-grekasnicolas-grekas merged commit0c7ba55 intosymfony:7.4Sep 25, 2025
11 of 12 checks passed
@VincentLanglet
Copy link
Contributor

new Uuid() can be used in doctrine entity in which it's not possible to use dependency injection.

In order to enjoy fully the MockUuidFactory, should we expose something similar toClock::get() ?@nicolas-grekas@OskarStark

@Hanmac
Copy link
Contributor

i don't know if it would be needed, but looking at the UuidFactory classes, i would suspect them to use Interfaces instead

Like should MockUuidFactory extend from UuidFactory if none of the functions and properties are used?
Or should there be a UuidFactoryInterface instead?

Same withRandomBasedUuidFactoryInterface and the others?

@nicolas-grekas
Copy link
Member

We'd rather figure out a way to have Doctrine use a factory instead.

@stof
Copy link
Member

if you use a custom id generator in the ORM, it can use any dependency it wants. Andsymfony/doctrine-bridge already implements theUuidGenerator and theUlidGenerator.

If you want to generate the id yourselves in the constructor of your entities, you cannot use dependency injection at all (as the instantiation is not managed by the DI component). But I don't think that's a reason to introduce a global state for accessing the configured factory.

andrew-demb reacted with thumbs up emoji

@VincentLanglet
Copy link
Contributor

If you want to generate the id yourselves in the constructor of your entities, you cannot use dependency injection at all (as the instantiation is not managed by the DI component). But I don't think that's a reason to introduce a global state for accessing the configured factory.

Indeed, I was doing in my entity

    public function __construct()    {        $this->uuid = Uuid::v7();        $this->createdAt = Clock::get()->now();    }

This was referencedOct 27, 2025
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@stofstofstof left review comments

@nicolas-grekasnicolas-grekasnicolas-grekas approved these changes

@OskarStarkOskarStarkOskarStark approved these changes

@alexandre-dauboisalexandre-dauboisalexandre-daubois approved these changes

Assignees

No one assigned

Projects

None yet

Milestone

7.4

Development

Successfully merging this pull request may close these issues.

[Uid] AddMockUuid/UuidFactory for deterministic UUID generation in tests

8 participants

@momito69@nicolas-grekas@VincentLanglet@Hanmac@stof@OskarStark@alexandre-daubois@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp