Passed
Pull Request — master (#216)
by Charly
03:45
created

ModelFactoryTest::doctrine_events_still_fired()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 11
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\Event\CommentEventListener;
11
use Zenstruck\Foundry\Tests\Fixtures\Event\CommentEventSubscriber;
12
use Zenstruck\Foundry\Tests\Fixtures\Event\PostEventSubscriber;
13
use Zenstruck\Foundry\Tests\Fixtures\Factories\AddressFactory;
14
use Zenstruck\Foundry\Tests\Fixtures\Factories\CategoryFactory;
15
use Zenstruck\Foundry\Tests\Fixtures\Factories\CommentFactory;
16
use Zenstruck\Foundry\Tests\Fixtures\Factories\ContactFactory;
17
use Zenstruck\Foundry\Tests\Fixtures\Factories\PostFactory;
18
use Zenstruck\Foundry\Tests\Fixtures\Factories\PostFactoryWithInvalidInitialize;
19
use Zenstruck\Foundry\Tests\Fixtures\Factories\PostFactoryWithNullInitialize;
20
use Zenstruck\Foundry\Tests\Fixtures\Factories\PostFactoryWithValidInitialize;
21
use Zenstruck\Foundry\Tests\Fixtures\Factories\TagFactory;
22
use Zenstruck\Foundry\Tests\Fixtures\Factories\UserFactory;
23
24
/**
25
 * @author Kevin Bond <[email protected]>
26
 */
27
final class ModelFactoryTest extends KernelTestCase
28
{
29
    use ContainerBC, Factories, ResetDatabase;
30
31
    /**
32
     * @test
33
     */
34
    public function can_find_or_create(): void
35
    {
36
        CategoryFactory::assert()->count(0);
37
        CategoryFactory::findOrCreate(['name' => 'php']);
38
        CategoryFactory::assert()->count(1);
39
        CategoryFactory::findOrCreate(['name' => 'php']);
40
        CategoryFactory::assert()->count(1);
41
    }
42
43
    /**
44
     * @test
45
     */
46
    public function can_override_initialize(): void
47
    {
48
        $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

48
        $this->assertFalse(PostFactory::createOne()->/** @scrutinizer ignore-call */ isPublished());
Loading history...
49
        $this->assertTrue(PostFactoryWithValidInitialize::createOne()->isPublished());
50
    }
51
52
    /**
53
     * @test
54
     */
55
    public function initialize_must_return_an_instance_of_the_current_factory(): void
56
    {
57
        $this->expectException(\TypeError::class);
58
        $this->expectExceptionMessage(\sprintf('"%1$s::initialize()" must return an instance of "%1$s".', PostFactoryWithInvalidInitialize::class));
59
60
        PostFactoryWithInvalidInitialize::new();
61
    }
62
63
    /**
64
     * @test
65
     */
66
    public function initialize_must_return_a_value(): void
67
    {
68
        $this->expectException(\TypeError::class);
69
        $this->expectExceptionMessage(\sprintf('"%1$s::initialize()" must return an instance of "%1$s".', PostFactoryWithNullInitialize::class));
70
71
        PostFactoryWithNullInitialize::new();
72
    }
73
74
    /**
75
     * @test
76
     */
77
    public function can_find_random_object(): void
78
    {
79
        CategoryFactory::createMany(5);
80
81
        $ids = [];
82
83
        while (5 !== \count(\array_unique($ids))) {
84
            $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

84
            $ids[] = CategoryFactory::random()->/** @scrutinizer ignore-call */ getId();
Loading history...
85
        }
86
87
        $this->assertCount(5, \array_unique($ids));
88
    }
89
90
    /**
91
     * @test
92
     */
93
    public function can_create_random_object_if_none_exists(): void
94
    {
95
        CategoryFactory::assert()->count(0);
96
        $this->assertInstanceOf(Category::class, CategoryFactory::randomOrCreate()->object());
97
        CategoryFactory::assert()->count(1);
98
        $this->assertInstanceOf(Category::class, CategoryFactory::randomOrCreate()->object());
99
        CategoryFactory::assert()->count(1);
100
    }
101
102
    /**
103
     * @test
104
     */
105
    public function can_get_or_create_random_object_with_attributes(): void
106
    {
107
        CategoryFactory::createMany(5, ['name' => 'name1']);
108
109
        CategoryFactory::assert()->count(5);
110
        $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

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

193
        $this->assertCount(4, $post->/** @scrutinizer ignore-call */ getComments());
Loading history...
194
        UserFactory::assert()->count(4);
195
        CommentFactory::assert()->count(4);
196
        PostFactory::assert()->count(1);
197
    }
198
199
    /**
200
     * @test
201
     */
202
    public function create_multiple_one_to_many_with_nested_collection_relationship(): void
203
    {
204
        $user = UserFactory::createOne();
205
        $posts = PostFactory::createMany(2, [
206
            'comments' => CommentFactory::new(['user' => $user])->many(4),
207
        ]);
208
209
        $this->assertCount(4, $posts[0]->getComments());
210
        $this->assertCount(4, $posts[1]->getComments());
211
        UserFactory::assert()->count(1);
212
        CommentFactory::assert()->count(8);
213
        PostFactory::assert()->count(2);
214
    }
215
216
    /**
217
     * @test
218
     */
219
    public function many_to_many_with_nested_collection_relationship(): void
220
    {
221
        $post = PostFactory::createOne([
222
            'tags' => TagFactory::new()->many(3),
223
        ]);
224
225
        $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

225
        $this->assertCount(3, $post->/** @scrutinizer ignore-call */ getTags());
Loading history...
226
        TagFactory::assert()->count(5); // 3 created by this test and 2 in global state
227
        PostFactory::assert()->count(1);
228
    }
229
230
    /**
231
     * @test
232
     */
233
    public function inverse_many_to_many_with_nested_collection_relationship(): void
234
    {
235
        $tag = TagFactory::createOne([
236
            'posts' => PostFactory::new()->many(3),
237
        ]);
238
239
        $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

239
        $this->assertCount(3, $tag->/** @scrutinizer ignore-call */ getPosts());
Loading history...
240
        TagFactory::assert()->count(3); // 1 created by this test and 2 in global state
241
        PostFactory::assert()->count(3);
242
    }
243
244
    /**
245
     * @test
246
     */
247
    public function create_multiple_many_to_many_with_nested_collection_relationship(): void
248
    {
249
        $posts = PostFactory::createMany(2, [
250
            'tags' => TagFactory::new()->many(3),
251
        ]);
252
253
        $this->assertCount(3, $posts[0]->getTags());
254
        $this->assertCount(3, $posts[1]->getTags());
255
        TagFactory::assert()->count(8); // 6 created by this test and 2 in global state
256
        PostFactory::assert()->count(2);
257
    }
258
259
    /**
260
     * @test
261
     */
262
    public function unpersisted_one_to_many_with_nested_collection_relationship(): void
263
    {
264
        $post = PostFactory::new()->withoutPersisting()->create([
265
            'comments' => CommentFactory::new()->many(4),
266
        ]);
267
268
        $this->assertCount(4, $post->getComments());
269
        UserFactory::assert()->empty();
270
        CommentFactory::assert()->empty();
271
        PostFactory::assert()->empty();
272
    }
273
274
    /**
275
     * @test
276
     */
277
    public function unpersisted_many_to_many_with_nested_collection_relationship(): void
278
    {
279
        $post = PostFactory::new()->withoutPersisting()->create([
280
            'tags' => TagFactory::new()->many(3),
281
        ]);
282
283
        $this->assertCount(3, $post->getTags());
284
        TagFactory::assert()->count(2); // 2 created in global state
285
        PostFactory::assert()->empty();
286
    }
287
288
    /**
289
     * @test
290
     * @dataProvider dataProvider
291
     */
292
    public function can_use_model_factories_in_a_data_provider(PostFactory $factory, bool $published): void
293
    {
294
        $post = $factory->create();
295
296
        $post->assertPersisted();
297
        $this->assertSame($published, $post->isPublished());
298
    }
299
300
    public static function dataProvider(): array
301
    {
302
        return [
303
            [PostFactory::new(), false],
304
            [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

304
            [PostFactory::new()->/** @scrutinizer ignore-call */ published(), true],
Loading history...
305
        ];
306
    }
307
308
    /**
309
     * @test
310
     */
311
    public function many_to_one_unmanaged_entity(): void
312
    {
313
        $category = CategoryFactory::createOne(['name' => 'My Category']);
314
315
        self::container()->get(EntityManagerInterface::class)->clear();
316
317
        $post = PostFactory::createOne(['category' => $category]);
318
319
        $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

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

457
        $this->assertSame('Some address', $object1->/** @scrutinizer ignore-call */ getValue());
Loading history...
458
        $this->assertSame('another address', $object2->getValue());
459
    }
460
461
    /**
462
     * @test
463
     */
464
    public function factory_with_embeddable(): void
465
    {
466
        ContactFactory::repository()->assert()->empty();
467
468
        $object = ContactFactory::createOne();
469
470
        ContactFactory::repository()->assert()->count(1);
471
        $this->assertSame('Sally', $object->getName());
472
        $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

472
        $this->assertSame('Some address', $object->/** @scrutinizer ignore-call */ getAddress()->getValue());
Loading history...
473
    }
474
475
    /**
476
     * @test
477
     */
478
    public function doctrine_events_still_fired(): void
479
    {
480
        $commentSubscriber = CommentFactory::new()->create([
481
            'body' => CommentEventSubscriber::COMMENT_BODY,
482
        ]);
483
        $commentListener = CommentFactory::new()->create([
484
            'body' => CommentEventListener::COMMENT_BODY,
485
        ]);
486
487
        $this->assertSame(CommentEventSubscriber::NEW_COMMENT_BODY, $commentSubscriber->getBody());
0 ignored issues
show
Bug introduced by
The method getBody() 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

487
        $this->assertSame(CommentEventSubscriber::NEW_COMMENT_BODY, $commentSubscriber->/** @scrutinizer ignore-call */ getBody());
Loading history...
488
        $this->assertSame(CommentEventListener::NEW_COMMENT_BODY, $commentListener->getBody());
489
    }
490
491
    /**
492
     * @test
493
     */
494
    public function disable_doctrine_events_lifecycle(): void
495
    {
496
        $commentSubscriber = CommentFactory::new()->withoutDoctrineEvents()->create([
497
            'body' => CommentEventSubscriber::COMMENT_BODY,
498
        ]);
499
        $commentListener = CommentFactory::new()->withoutDoctrineEvents()->create([
500
            'body' => CommentEventListener::COMMENT_BODY,
501
        ]);
502
503
        $this->assertSame(CommentEventSubscriber::COMMENT_BODY, $commentSubscriber->getBody());
504
        $this->assertSame(CommentEventListener::COMMENT_BODY, $commentListener->getBody());
505
506
        $commentSubscriber2 = CommentFactory::new()->create([
507
            'body' => CommentEventSubscriber::COMMENT_BODY,
508
        ]);
509
        $commentListener2 = CommentFactory::new()->create([
510
            'body' => CommentEventListener::COMMENT_BODY,
511
        ]);
512
513
        $this->assertSame(CommentEventSubscriber::NEW_COMMENT_BODY, $commentSubscriber2->getBody());
514
        $this->assertSame(CommentEventListener::NEW_COMMENT_BODY, $commentListener2->getBody());
515
    }
516
517
    /**
518
     * @test
519
     */
520
    public function disable_doctrine_events_lifecycle_for_sub_factories(): void
521
    {
522
        $tag1 = TagFactory::new()
523
            ->withoutDoctrineEvents()
524
            ->create(['posts' => PostFactory::new(['title' => PostEventSubscriber::TITLE_VALUE])->many(4)])
525
        ;
526
        $this->assertSame(PostEventSubscriber::TITLE_VALUE, $tag1->getPosts()[0]->getTitle());
527
528
        $tag2 = TagFactory::new()
529
            ->create(['posts' => PostFactory::new(['title' => PostEventSubscriber::TITLE_VALUE])->many(4)])
530
        ;
531
        $this->assertSame(PostEventSubscriber::NEW_TITLE_VALUE, $tag2->getPosts()[0]->getTitle());
532
533
        $posts = PostFactory::createMany(4, ['title' => PostEventSubscriber::TITLE_VALUE]);
534
        $tag3 = TagFactory::new()
535
            ->withoutDoctrineEvents()
536
            ->create(['posts' => $posts])
537
        ;
538
        $this->assertSame(PostEventSubscriber::NEW_TITLE_VALUE, $tag3->getPosts()[0]->getTitle());
539
    }
540
}
541