1111
1212namespace Zenstruck \Foundry \Persistence ;
1313
14+ use PHPUnit \Event \Code \ClassMethod ;
15+
1416/**
15- * If a persistent object has been created in a data provider, we need to initialize the proxy object,
17+ * If a persistent object has been created in a data provider, we need to initialize the lazy object,
1618 * which will trigger the object to be persisted.
1719 *
18- * Otherwise, such test would not pass:
20+ * Otherwise, such a test would not pass:
1921 * ```php
2022 * #[DataProvider('provide')]
2123 * public function testSomething(MyEntity $entity): void
3133 *
3234 * Sadly, this cannot be done directly a subscriber, since PHPUnit does not give access to the actual tests instances.
3335 *
34- * This class is highly hacky!
35- * We collect all the "datasets" and we trigger the persistence for each one before the test is executed.
36- * This means that de data providers are called twice.
36+ * ⚠️ This class is highly hacky!
37+ *
38+ * If we detect that a persisting object was created in a data provider, we collect the "datasets" of the test,
39+ * and we trigger the persistence of these objects before the test is executed.
40+ *
41+ * This means that the data providers using Foundry are called twice.
3742 * To prevent the persisted object from being different from the one returned by the data provider, we use a "buffer" so
3843 * that we can return the same object for each data provider call.
3944 *
@@ -49,30 +54,39 @@ final class PersistentObjectFromDataProviderRegistry
4954 /** @var list<object> */
5055 private array $ objectsBuffer = [];
5156
52- private bool $ shouldReturnExistingObject = false ;
57+ private bool $ shouldReturnObjectFromBuffer = false ;
5358
5459 public static function instance (): self
5560 {
5661 return self ::$ instance ?? self ::$ instance = new self ();
5762 }
5863
59- /**
60- * @param callable():iterable<array-key, mixed> $dataProviderResult
61- */
62- public function addDataset (string $ className , string $ methodName , callable $ dataProviderResult ): void
64+ public function storeDatasetIfFoundryWasUsedInDataProvider (string $ className , string $ methodName , ClassMethod ...$ calledMethods ): void
6365 {
64- $ this ->shouldReturnExistingObject = false ;
66+ if (count ($ this ->objectsBuffer ) === 0 ) {
67+ return ;
68+ }
69+
70+ $ this ->shouldReturnObjectFromBuffer = true ;
71+
72+ $ testCaseContext = $ this ->testCaseContext ($ className , $ methodName );
73+ $ this ->datasets [$ testCaseContext ] = [];
74+
75+ foreach ($ calledMethods as $ calledMethod ) {
76+ $ dataProviderResult = "{$ calledMethod ->className ()}:: {$ calledMethod ->methodName ()}" (); // @phpstan-ignore callable.nonCallable
6577
66- $ dataProviderResult = $ dataProviderResult ();
78+ if (!\is_array ($ dataProviderResult )) {
79+ $ dataProviderResult = \iterator_to_array ($ dataProviderResult );
80+ }
6781
68- if (!\is_array ($ dataProviderResult )) {
69- $ dataProviderResult = \iterator_to_array ($ dataProviderResult );
82+ $ this ->datasets [$ testCaseContext ] = [...$ this ->datasets [$ testCaseContext ], ...$ dataProviderResult ];
7083 }
7184
72- $ testCaseContext = $ this ->testCaseContext ($ className , $ methodName );
73- $ this ->datasets [$ testCaseContext ] = $ dataProviderResult ;
85+ $ this ->shouldReturnObjectFromBuffer = false ;
7486
75- $ this ->shouldReturnExistingObject = true ;
87+ if (count ($ this ->objectsBuffer ) !== 0 ) { // @phpstan-ignore notIdentical.alwaysTrue
88+ throw new \InvalidArgumentException ("No object found. Hint: make sure you're not creating a randomized number of objects with Foundry in a data provider, as they are not supported. " );
89+ }
7690 }
7791
7892 /**
@@ -84,10 +98,18 @@ public function addDataset(string $className, string $methodName, callable $data
8498 */
8599 public function deferObjectCreation (PersistentObjectFactory $ factory ): object
86100 {
87- if (!$ this ->shouldReturnExistingObject ) {
101+ if (!$ factory ->isPersisting ()) {
102+ return $ factory ->create ();
103+ }
104+
105+ if (!$ this ->shouldReturnObjectFromBuffer ) {
88106 return $ this ->objectsBuffer [] = ProxyGenerator::wrapFactory ($ factory );
89107 }
90108
109+ if (count ($ this ->objectsBuffer ) === 0 ) {
110+ throw new \InvalidArgumentException ("No object found. Hint: make sure you're not creating a randomized number of objects with Foundry in a data provider, as they are not supported. " );
111+ }
112+
91113 return \array_shift ($ this ->objectsBuffer ); // @phpstan-ignore return.type
92114 }
93115
@@ -99,7 +121,7 @@ public function triggerPersistenceForDataset(string $className, string $methodNa
99121 return ;
100122 }
101123
102- initialize_proxy_object ($ this ->datasets [$ testCaseContext ][$ dataSetName ]);
124+ initialize_lazy_object ($ this ->datasets [$ testCaseContext ][$ dataSetName ]);
103125
104126 unset($ this ->datasets [$ testCaseContext ][$ dataSetName ]);
105127 }
0 commit comments