Passed
Push — master ( 5f39d8...1dccda )
by Kevin
02:49
created

ModelFactoryTest::factory_with_embeddable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 9
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Zenstruck\Foundry\Tests\Functional;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
7
use Zenstruck\Foundry\Test\Factories;
8
use Zenstruck\Foundry\Test\ResetDatabase;
9
use Zenstruck\Foundry\Tests\Fixtures\Entity\Category;
10
use Zenstruck\Foundry\Tests\Fixtures\Factories\AddressFactory;
11
use Zenstruck\Foundry\Tests\Fixtures\Factories\CategoryFactory;
12
use Zenstruck\Foundry\Tests\Fixtures\Factories\CommentFactory;
13
use Zenstruck\Foundry\Tests\Fixtures\Factories\ContactFactory;
14
use Zenstruck\Foundry\Tests\Fixtures\Factories\PostFactory;
15
use Zenstruck\Foundry\Tests\Fixtures\Factories\PostFactoryWithInvalidInitialize;
16
use Zenstruck\Foundry\Tests\Fixtures\Factories\PostFactoryWithNullInitialize;
17
use Zenstruck\Foundry\Tests\Fixtures\Factories\PostFactoryWithValidInitialize;
18
use Zenstruck\Foundry\Tests\Fixtures\Factories\TagFactory;
19
use Zenstruck\Foundry\Tests\Fixtures\Factories\UserFactory;
20
21
/**
22
 * @author Kevin Bond <[email protected]>
23
 */
24
final class ModelFactoryTest extends KernelTestCase
25
{
26
    use Factories, ResetDatabase;
27
28
    /**
29
     * @test
30
     */
31
    public function can_find_or_create(): void
32
    {
33
        CategoryFactory::assert()->count(0);
34
        CategoryFactory::findOrCreate(['name' => 'php']);
35
        CategoryFactory::assert()->count(1);
36
        CategoryFactory::findOrCreate(['name' => 'php']);
37
        CategoryFactory::assert()->count(1);
38
    }
39
40
    /**
41
     * @test
42
     */
43
    public function can_override_initialize(): void
44
    {
45
        $this->assertFalse(PostFactory::createOne()->isPublished());
0 ignored issues
show
Bug introduced by
The method isPublished() does not exist on Zenstruck\Foundry\Proxy. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

45
        $this->assertFalse(PostFactory::createOne()->/** @scrutinizer ignore-call */ isPublished());
Loading history...
46
        $this->assertTrue(PostFactoryWithValidInitialize::createOne()->isPublished());
47
    }
48
49
    /**
50
     * @test
51
     */
52
    public function initialize_must_return_an_instance_of_the_current_factory(): void
53
    {
54
        $this->expectException(\TypeError::class);
55
        $this->expectExceptionMessage(\sprintf('"%1$s::initialize()" must return an instance of "%1$s".', PostFactoryWithInvalidInitialize::class));
56
57
        PostFactoryWithInvalidInitialize::new();
58
    }
59
60
    /**
61
     * @test
62
     */
63
    public function initialize_must_return_a_value(): void
64
    {
65
        $this->expectException(\TypeError::class);
66
        $this->expectExceptionMessage(\sprintf('"%1$s::initialize()" must return an instance of "%1$s".', PostFactoryWithNullInitialize::class));
67
68
        PostFactoryWithNullInitialize::new();
69
    }
70
71
    /**
72
     * @test
73
     */
74
    public function can_find_random_object(): void
75
    {
76
        CategoryFactory::createMany(5);
77
78
        $ids = [];
79
80
        while (5 !== \count(\array_unique($ids))) {
81
            $ids[] = CategoryFactory::random()->getId();
0 ignored issues
show
Bug introduced by
The method getId() does not exist on Zenstruck\Foundry\Proxy. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

81
            $ids[] = CategoryFactory::random()->/** @scrutinizer ignore-call */ getId();
Loading history...
82
        }
83
84
        $this->assertCount(5, \array_unique($ids));
85
    }
86
87
    /**
88
     * @test
89
     */
90
    public function can_create_random_object_if_none_exists(): void
91
    {
92
        CategoryFactory::assert()->count(0);
93
        $this->assertInstanceOf(Category::class, CategoryFactory::randomOrCreate()->object());
94
        CategoryFactory::assert()->count(1);
95
        $this->assertInstanceOf(Category::class, CategoryFactory::randomOrCreate()->object());
96
        CategoryFactory::assert()->count(1);
97
    }
98
99
    /**
100
     * @test
101
     */
102
    public function can_get_or_create_random_object_with_attributes(): void
103
    {
104
        CategoryFactory::createMany(5, ['name' => 'name1']);
105
106
        CategoryFactory::assert()->count(5);
107
        $this->assertSame('name2', CategoryFactory::randomOrCreate(['name' => 'name2'])->getName());
0 ignored issues
show
Bug introduced by
The method getName() does not exist on Zenstruck\Foundry\Proxy. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

107
        $this->assertSame('name2', CategoryFactory::randomOrCreate(['name' => 'name2'])->/** @scrutinizer ignore-call */ getName());
Loading history...
108
        CategoryFactory::assert()->count(6);
109
        $this->assertSame('name2', CategoryFactory::randomOrCreate(['name' => 'name2'])->getName());
110
        CategoryFactory::assert()->count(6);
111
    }
112
113
    /**
114
     * @test
115
     */
116
    public function can_find_random_set_of_objects(): void
117
    {
118
        CategoryFactory::createMany(5);
119
120
        $objects = CategoryFactory::randomSet(3);
121
122
        $this->assertCount(3, $objects);
123
        $this->assertCount(3, \array_unique(\array_map(static function($category) { return $category->getId(); }, $objects)));
124
    }
125
126
    /**
127
     * @test
128
     */
129
    public function can_find_random_set_of_objects_with_attributes(): void
130
    {
131
        CategoryFactory::createMany(20, ['name' => 'name1']);
132
        CategoryFactory::createMany(5, ['name' => 'name2']);
133
134
        $objects = CategoryFactory::randomSet(2, ['name' => 'name2']);
135
136
        $this->assertCount(2, $objects);
137
        $this->assertSame('name2', $objects[0]->getName());
138
        $this->assertSame('name2', $objects[1]->getName());
139
    }
140
141
    /**
142
     * @test
143
     */
144
    public function can_find_random_range_of_objects(): void
145
    {
146
        CategoryFactory::createMany(5);
147
148
        $counts = [];
149
150
        while (4 !== \count(\array_unique($counts))) {
151
            $counts[] = \count(CategoryFactory::randomRange(0, 3));
152
        }
153
154
        $this->assertCount(4, \array_unique($counts));
155
        $this->assertContains(0, $counts);
156
        $this->assertContains(1, $counts);
157
        $this->assertContains(2, $counts);
158
        $this->assertContains(3, $counts);
159
        $this->assertNotContains(4, $counts);
160
        $this->assertNotContains(5, $counts);
161
    }
162
163
    /**
164
     * @test
165
     */
166
    public function can_find_random_range_of_objects_with_attributes(): void
167
    {
168
        CategoryFactory::createMany(20, ['name' => 'name1']);
169
        CategoryFactory::createMany(5, ['name' => 'name2']);
170
171
        $objects = CategoryFactory::randomRange(2, 4, ['name' => 'name2']);
172
173
        $this->assertGreaterThanOrEqual(2, \count($objects));
174
        $this->assertLessThanOrEqual(4, \count($objects));
175
176
        foreach ($objects as $object) {
177
            $this->assertSame('name2', $object->getName());
178
        }
179
    }
180
181
    /**
182
     * @test
183
     */
184
    public function one_to_many_with_nested_collection_relationship(): void
185
    {
186
        $post = PostFactory::createOne([
187
            'comments' => CommentFactory::new()->many(4),
188
        ]);
189
190
        $this->assertCount(4, $post->getComments());
0 ignored issues
show
Bug introduced by
The method getComments() does not exist on Zenstruck\Foundry\Proxy. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

190
        $this->assertCount(4, $post->/** @scrutinizer ignore-call */ getComments());
Loading history...
191
        UserFactory::assert()->count(4);
192
        CommentFactory::assert()->count(4);
193
        PostFactory::assert()->count(1);
194
    }
195
196
    /**
197
     * @test
198
     */
199
    public function create_multiple_one_to_many_with_nested_collection_relationship(): void
200
    {
201
        $user = UserFactory::createOne();
202
        $posts = PostFactory::createMany(2, [
203
            'comments' => CommentFactory::new(['user' => $user])->many(4),
204
        ]);
205
206
        $this->assertCount(4, $posts[0]->getComments());
207
        $this->assertCount(4, $posts[1]->getComments());
208
        UserFactory::assert()->count(1);
209
        CommentFactory::assert()->count(8);
210
        PostFactory::assert()->count(2);
211
    }
212
213
    /**
214
     * @test
215
     */
216
    public function many_to_many_with_nested_collection_relationship(): void
217
    {
218
        $post = PostFactory::createOne([
219
            'tags' => TagFactory::new()->many(3),
220
        ]);
221
222
        $this->assertCount(3, $post->getTags());
0 ignored issues
show
Bug introduced by
The method getTags() does not exist on Zenstruck\Foundry\Proxy. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

222
        $this->assertCount(3, $post->/** @scrutinizer ignore-call */ getTags());
Loading history...
223
        TagFactory::assert()->count(5); // 3 created by this test and 2 in global state
224
        PostFactory::assert()->count(1);
225
    }
226
227
    /**
228
     * @test
229
     */
230
    public function inverse_many_to_many_with_nested_collection_relationship(): void
231
    {
232
        $tag = TagFactory::createOne([
233
            'posts' => PostFactory::new()->many(3),
234
        ]);
235
236
        $this->assertCount(3, $tag->getPosts());
0 ignored issues
show
Bug introduced by
The method getPosts() does not exist on Zenstruck\Foundry\Proxy. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

236
        $this->assertCount(3, $tag->/** @scrutinizer ignore-call */ getPosts());
Loading history...
237
        TagFactory::assert()->count(3); // 1 created by this test and 2 in global state
238
        PostFactory::assert()->count(3);
239
    }
240
241
    /**
242
     * @test
243
     */
244
    public function create_multiple_many_to_many_with_nested_collection_relationship(): void
245
    {
246
        $posts = PostFactory::createMany(2, [
247
            'tags' => TagFactory::new()->many(3),
248
        ]);
249
250
        $this->assertCount(3, $posts[0]->getTags());
251
        $this->assertCount(3, $posts[1]->getTags());
252
        TagFactory::assert()->count(8); // 6 created by this test and 2 in global state
253
        PostFactory::assert()->count(2);
254
    }
255
256
    /**
257
     * @test
258
     */
259
    public function unpersisted_one_to_many_with_nested_collection_relationship(): void
260
    {
261
        $post = PostFactory::new()->withoutPersisting()->create([
262
            'comments' => CommentFactory::new()->many(4),
263
        ]);
264
265
        $this->assertCount(4, $post->getComments());
266
        UserFactory::assert()->empty();
267
        CommentFactory::assert()->empty();
268
        PostFactory::assert()->empty();
269
    }
270
271
    /**
272
     * @test
273
     */
274
    public function unpersisted_many_to_many_with_nested_collection_relationship(): void
275
    {
276
        $post = PostFactory::new()->withoutPersisting()->create([
277
            'tags' => TagFactory::new()->many(3),
278
        ]);
279
280
        $this->assertCount(3, $post->getTags());
281
        TagFactory::assert()->count(2); // 2 created in global state
282
        PostFactory::assert()->empty();
283
    }
284
285
    /**
286
     * @test
287
     * @dataProvider dataProvider
288
     */
289
    public function can_use_model_factories_in_a_data_provider(PostFactory $factory, bool $published): void
290
    {
291
        $post = $factory->create();
292
293
        $post->assertPersisted();
294
        $this->assertSame($published, $post->isPublished());
295
    }
296
297
    public static function dataProvider(): array
298
    {
299
        return [
300
            [PostFactory::new(), false],
301
            [PostFactory::new()->published(), true],
0 ignored issues
show
Bug introduced by
The method published() does not exist on Zenstruck\Foundry\ModelFactory. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

301
            [PostFactory::new()->/** @scrutinizer ignore-call */ published(), true],
Loading history...
302
        ];
303
    }
304
305
    /**
306
     * @test
307
     */
308
    public function many_to_one_unmanaged_entity(): void
309
    {
310
        $category = CategoryFactory::createOne(['name' => 'My Category']);
311
312
        self::$container->get(EntityManagerInterface::class)->clear();
313
314
        $post = PostFactory::createOne(['category' => $category]);
315
316
        $this->assertSame('My Category', $post->getCategory()->getName());
0 ignored issues
show
Bug introduced by
The method getCategory() does not exist on Zenstruck\Foundry\Proxy. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

316
        $this->assertSame('My Category', $post->/** @scrutinizer ignore-call */ getCategory()->getName());
Loading history...
317
    }
318
319
    /**
320
     * @test
321
     */
322
    public function many_to_one_unmanaged_raw_entity(): void
323
    {
324
        $category = CategoryFactory::createOne(['name' => 'My Category'])->object();
325
326
        self::$container->get(EntityManagerInterface::class)->clear();
327
328
        $post = PostFactory::createOne(['category' => $category]);
329
330
        $this->assertSame('My Category', $post->getCategory()->getName());
331
    }
332
333
    /**
334
     * @test
335
     */
336
    public function first_and_last_return_the_correct_object(): void
337
    {
338
        $categoryA = CategoryFactory::createOne(['name' => '3']);
339
        $categoryB = CategoryFactory::createOne(['name' => '2']);
0 ignored issues
show
Unused Code introduced by
The assignment to $categoryB is dead and can be removed.
Loading history...
340
        $categoryC = CategoryFactory::createOne(['name' => '1']);
341
342
        $this->assertSame($categoryA->getId(), CategoryFactory::first()->getId());
343
        $this->assertSame($categoryC->getId(), CategoryFactory::first('name')->getId());
344
        $this->assertSame($categoryC->getId(), CategoryFactory::last()->getId());
345
        $this->assertSame($categoryA->getId(), CategoryFactory::last('name')->getId());
346
    }
347
348
    /**
349
     * @test
350
     */
351
    public function first_throws_exception_if_no_entities_exist(): void
352
    {
353
        $this->expectException(\RuntimeException::class);
354
355
        CategoryFactory::first();
356
    }
357
358
    /**
359
     * @test
360
     */
361
    public function last_throws_exception_if_no_entities_exist(): void
362
    {
363
        $this->expectException(\RuntimeException::class);
364
365
        CategoryFactory::last();
366
    }
367
368
    /**
369
     * @test
370
     */
371
    public function can_count_and_truncate_model_factory(): void
372
    {
373
        $this->assertSame(0, CategoryFactory::count());
374
375
        CategoryFactory::createMany(4);
376
377
        $this->assertSame(4, CategoryFactory::count());
378
379
        CategoryFactory::truncate();
380
381
        $this->assertSame(0, CategoryFactory::count());
382
    }
383
384
    /**
385
     * @test
386
     */
387
    public function can_get_all_entities(): void
388
    {
389
        $this->assertSame([], CategoryFactory::all());
390
391
        CategoryFactory::createMany(4);
392
393
        $categories = CategoryFactory::all();
394
395
        $this->assertCount(4, $categories);
396
        $this->assertInstanceOf(Category::class, $categories[0]->object());
397
        $this->assertInstanceOf(Category::class, $categories[1]->object());
398
        $this->assertInstanceOf(Category::class, $categories[2]->object());
399
        $this->assertInstanceOf(Category::class, $categories[3]->object());
400
    }
401
402
    /**
403
     * @test
404
     */
405
    public function can_find_entity(): void
406
    {
407
        CategoryFactory::createOne(['name' => 'first']);
408
        CategoryFactory::createOne(['name' => 'second']);
409
        $category = CategoryFactory::createOne(['name' => 'third']);
410
411
        $this->assertSame('second', CategoryFactory::find(['name' => 'second'])->getName());
412
        $this->assertSame('third', CategoryFactory::find(['id' => $category->getId()])->getName());
413
        $this->assertSame('third', CategoryFactory::find($category->getId())->getName());
414
        $this->assertSame('third', CategoryFactory::find($category->object())->getName());
415
        $this->assertSame('third', CategoryFactory::find($category)->getName());
416
    }
417
418
    /**
419
     * @test
420
     */
421
    public function find_throws_exception_if_no_entities_exist(): void
422
    {
423
        $this->expectException(\RuntimeException::class);
424
425
        CategoryFactory::find(99);
426
    }
427
428
    /**
429
     * @test
430
     */
431
    public function can_find_by(): void
432
    {
433
        $this->assertSame([], CategoryFactory::findBy(['name' => 'name2']));
434
435
        CategoryFactory::createOne(['name' => 'name1']);
436
        CategoryFactory::createOne(['name' => 'name2']);
437
        CategoryFactory::createOne(['name' => 'name2']);
438
439
        $categories = CategoryFactory::findBy(['name' => 'name2']);
440
441
        $this->assertCount(2, $categories);
442
        $this->assertSame('name2', $categories[0]->getName());
443
        $this->assertSame('name2', $categories[1]->getName());
444
    }
445
446
    /**
447
     * @test
448
     */
449
    public function embeddables_are_never_persisted(): void
450
    {
451
        $object1 = AddressFactory::createOne();
452
        $object2 = AddressFactory::createOne(['value' => 'another address']);
453
454
        $this->assertSame('Some address', $object1->getValue());
0 ignored issues
show
Bug introduced by
The method getValue() does not exist on Zenstruck\Foundry\Proxy. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

454
        $this->assertSame('Some address', $object1->/** @scrutinizer ignore-call */ getValue());
Loading history...
455
        $this->assertSame('another address', $object2->getValue());
456
    }
457
458
    /**
459
     * @test
460
     */
461
    public function factory_with_embeddable(): void
462
    {
463
        ContactFactory::repository()->assert()->empty();
464
465
        $object = ContactFactory::createOne();
466
467
        ContactFactory::repository()->assert()->count(1);
468
        $this->assertSame('Sally', $object->getName());
469
        $this->assertSame('Some address', $object->getAddress()->getValue());
0 ignored issues
show
Bug introduced by
The method getAddress() does not exist on Zenstruck\Foundry\Proxy. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

469
        $this->assertSame('Some address', $object->/** @scrutinizer ignore-call */ getAddress()->getValue());
Loading history...
470
    }
471
}
472