Passed
Pull Request — master (#1)
by Kevin
02:47
created

FactoryTest::can_create_object()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
nc 1
nop 0
dl 0
loc 16
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
namespace Zenstruck\Foundry\Tests\Unit;
4
5
use Doctrine\Persistence\ManagerRegistry;
6
use Doctrine\Persistence\ObjectManager;
7
use Faker;
8
use PHPUnit\Framework\TestCase;
9
use Zenstruck\Foundry\Factory;
10
use Zenstruck\Foundry\PersistenceManager;
11
use Zenstruck\Foundry\Proxy;
12
use Zenstruck\Foundry\Tests\Fixtures\Entity\Category;
13
use Zenstruck\Foundry\Tests\Fixtures\Entity\Post;
14
use Zenstruck\Foundry\Tests\ResetGlobals;
15
16
/**
17
 * @author Kevin Bond <[email protected]>
18
 */
19
final class FactoryTest extends TestCase
20
{
21
    use ResetGlobals;
22
23
    /**
24
     * @test
25
     */
26
    public function can_instantiate_object(): void
27
    {
28
        $attributeArray = ['title' => 'title', 'body' => 'body'];
29
        $attributeCallback = fn(Faker\Generator $faker) => ['title' => 'title', 'body' => 'body'];
0 ignored issues
show
Unused Code introduced by
The parameter $faker is not used and could be removed. ( Ignorable by Annotation )

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

29
        $attributeCallback = fn(/** @scrutinizer ignore-unused */ Faker\Generator $faker) => ['title' => 'title', 'body' => 'body'];

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
30
31
        $this->assertSame('title', (new Factory(Post::class, $attributeArray))->instantiate()->getTitle());
32
        $this->assertSame('title', (new Factory(Post::class))->instantiate($attributeArray)->getTitle());
33
        $this->assertSame('title', (new Factory(Post::class))->withAttributes($attributeArray)->instantiate()->getTitle());
34
        $this->assertSame('title', (new Factory(Post::class, $attributeCallback))->instantiate()->getTitle());
35
        $this->assertSame('title', (new Factory(Post::class))->instantiate($attributeCallback)->getTitle());
36
        $this->assertSame('title', (new Factory(Post::class))->withAttributes($attributeCallback)->instantiate()->getTitle());
37
    }
38
39
    /**
40
     * @test
41
     */
42
    public function can_instantiate_many_objects(): void
43
    {
44
        $attributeArray = ['title' => 'title', 'body' => 'body'];
45
        $attributeCallback = fn(Faker\Generator $faker) => ['title' => 'title', 'body' => 'body'];
0 ignored issues
show
Unused Code introduced by
The parameter $faker is not used and could be removed. ( Ignorable by Annotation )

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

45
        $attributeCallback = fn(/** @scrutinizer ignore-unused */ Faker\Generator $faker) => ['title' => 'title', 'body' => 'body'];

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
46
47
        $objects = (new Factory(Post::class, $attributeArray))->instantiateMany(3);
48
49
        $this->assertCount(3, $objects);
50
        $this->assertSame('title', $objects[0]->getTitle());
51
        $this->assertSame('title', $objects[1]->getTitle());
52
        $this->assertSame('title', $objects[2]->getTitle());
53
54
        $objects = (new Factory(Post::class))->instantiateMany(3, $attributeArray);
55
56
        $this->assertCount(3, $objects);
57
        $this->assertSame('title', $objects[0]->getTitle());
58
        $this->assertSame('title', $objects[1]->getTitle());
59
        $this->assertSame('title', $objects[2]->getTitle());
60
61
        $objects = (new Factory(Post::class))->withAttributes($attributeArray)->instantiateMany(3);
62
63
        $this->assertCount(3, $objects);
64
        $this->assertSame('title', $objects[0]->getTitle());
65
        $this->assertSame('title', $objects[1]->getTitle());
66
        $this->assertSame('title', $objects[2]->getTitle());
67
68
        $objects = (new Factory(Post::class, $attributeCallback))->instantiateMany(3);
69
70
        $this->assertCount(3, $objects);
71
        $this->assertSame('title', $objects[0]->getTitle());
72
        $this->assertSame('title', $objects[1]->getTitle());
73
        $this->assertSame('title', $objects[2]->getTitle());
74
75
        $objects = (new Factory(Post::class))->instantiateMany(3, $attributeCallback);
76
77
        $this->assertCount(3, $objects);
78
        $this->assertSame('title', $objects[0]->getTitle());
79
        $this->assertSame('title', $objects[1]->getTitle());
80
        $this->assertSame('title', $objects[2]->getTitle());
81
82
        $objects = (new Factory(Post::class))->withAttributes($attributeCallback)->instantiateMany(3);
83
84
        $this->assertCount(3, $objects);
85
        $this->assertSame('title', $objects[0]->getTitle());
86
        $this->assertSame('title', $objects[1]->getTitle());
87
        $this->assertSame('title', $objects[2]->getTitle());
88
    }
89
90
    /**
91
     * @test
92
     */
93
    public function can_set_instantiator(): void
94
    {
95
        $attributeArray = ['title' => 'original title', 'body' => 'original body'];
96
97
        $object = (new Factory(Post::class))->instantiator(function(array $attributes, string $class) use ($attributeArray) {
98
            $this->assertSame(Post::class, $class);
99
            $this->assertSame($attributes, $attributeArray);
100
101
            return new Post('title', 'body');
102
        })->instantiate($attributeArray);
103
104
        $this->assertSame('title', $object->getTitle());
105
        $this->assertSame('body', $object->getBody());
106
    }
107
108
    /**
109
     * @test
110
     */
111
    public function can_add_before_instantiate_events(): void
112
    {
113
        $attributeArray = ['title' => 'original title', 'body' => 'original body'];
114
115
        $object = (new Factory(Post::class))
116
            ->beforeInstantiate(function(array $attributes) {
117
                $attributes['title'] = 'title';
118
119
                return $attributes;
120
            })
121
            ->beforeInstantiate(function(array $attributes) {
122
                $attributes['body'] = 'body';
123
124
                return $attributes;
125
            })
126
            ->instantiate($attributeArray)
127
        ;
128
129
        $this->assertSame('title', $object->getTitle());
130
        $this->assertSame('body', $object->getBody());
131
    }
132
133
    /**
134
     * @test
135
     */
136
    public function before_instantiate_event_must_return_an_array(): void
137
    {
138
        $this->expectException(\LogicException::class);
139
        $this->expectExceptionMessage('Before Instantiate event callback must return an array.');
140
141
        (new Factory(Post::class))->beforeInstantiate(function() {})->instantiate();
142
    }
143
144
    /**
145
     * @test
146
     */
147
    public function can_add_after_instantiate_events(): void
148
    {
149
        $attributesArray = ['title' => 'title', 'body' => 'body'];
150
151
        $object = (new Factory(Post::class))
152
            ->afterInstantiate(function(Post $post, array $attributes) use ($attributesArray) {
153
                $this->assertSame($attributesArray, $attributes);
154
155
                $post->increaseViewCount();
156
            })
157
            ->afterInstantiate(function(Post $post, array $attributes) use ($attributesArray) {
158
                $this->assertSame($attributesArray, $attributes);
159
160
                $post->increaseViewCount();
161
            })
162
            ->instantiate($attributesArray)
163
        ;
164
165
        $this->assertSame(2, $object->getViewCount());
166
    }
167
168
    /**
169
     * @test
170
     */
171
    public function can_register_custom_faker(): void
172
    {
173
        $faker = Factory::faker();
174
175
        Factory::registerFaker(new Faker\Generator());
176
177
        $this->assertNotSame(\spl_object_id(Factory::faker()), \spl_object_id($faker));
178
    }
179
180
    /**
181
     * @test
182
     */
183
    public function can_register_default_instantiator(): void
184
    {
185
        Factory::registerDefaultInstantiator(function() {
186
            return new Post('different title', 'different body');
187
        });
188
189
        $object = (new Factory(Post::class, ['title' => 'title', 'body' => 'body']))->instantiate();
190
191
        $this->assertSame('different title', $object->getTitle());
192
        $this->assertSame('different body', $object->getBody());
193
    }
194
195
    /**
196
     * @test
197
     */
198
    public function instantiating_with_proxy_attribute_normalizes_to_underlying_object(): void
199
    {
200
        $object = (new Factory(Post::class))->instantiate([
201
            'title' => 'title',
202
            'body' => 'body',
203
            'category' => (new Proxy(new Category()))->withoutAutoRefresh(),
204
        ]);
205
206
        $this->assertInstanceOf(Category::class, $object->getCategory());
207
    }
208
209
    /**
210
     * @test
211
     */
212
    public function instantiating_with_factory_attribute_instantiates_the_factory(): void
213
    {
214
        $object = (new Factory(Post::class))->instantiate([
215
            'title' => 'title',
216
            'body' => 'body',
217
            'category' => new Factory(Category::class),
218
        ]);
219
220
        $this->assertInstanceOf(Category::class, $object->getCategory());
221
    }
222
223
    /**
224
     * @test
225
     */
226
    public function factory_is_immutable(): void
227
    {
228
        $factory = new Factory(Post::class);
229
        $objectId = \spl_object_id($factory);
230
231
        $this->assertNotSame(\spl_object_id($factory->withAttributes([])), $objectId);
232
        $this->assertNotSame(\spl_object_id($factory->instantiator(function() {})), $objectId);
233
        $this->assertNotSame(\spl_object_id($factory->beforeInstantiate(function() {})), $objectId);
234
        $this->assertNotSame(\spl_object_id($factory->afterInstantiate(function() {})), $objectId);
235
        $this->assertNotSame(\spl_object_id($factory->afterPersist(function() {})), $objectId);
236
    }
237
238
    /**
239
     * @test
240
     */
241
    public function can_create_object(): void
242
    {
243
        $registry = $this->createMock(ManagerRegistry::class);
244
        $registry
245
            ->expects($this->once())
246
            ->method('getManagerForClass')
247
            ->with(Post::class)
248
            ->willReturn($this->createMock(ObjectManager::class))
249
        ;
250
251
        PersistenceManager::register($registry);
252
253
        $object = (new Factory(Post::class))->create(['title' => 'title', 'body' => 'body']);
254
255
        $this->assertInstanceOf(Proxy::class, $object);
256
        $this->assertSame('title', $object->withoutAutoRefresh()->getTitle());
0 ignored issues
show
Bug introduced by
The method getTitle() 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

256
        $this->assertSame('title', $object->withoutAutoRefresh()->/** @scrutinizer ignore-call */ getTitle());
Loading history...
257
    }
258
259
    /**
260
     * @test
261
     */
262
    public function can_create_many_objects(): void
263
    {
264
        $registry = $this->createMock(ManagerRegistry::class);
265
        $registry
266
            ->expects($this->exactly(3))
267
            ->method('getManagerForClass')
268
            ->with(Post::class)
269
            ->willReturn($this->createMock(ObjectManager::class))
270
        ;
271
272
        PersistenceManager::register($registry);
273
274
        $objects = (new Factory(Post::class))->createMany(3, ['title' => 'title', 'body' => 'body']);
275
276
        $this->assertCount(3, $objects);
277
        $this->assertInstanceOf(Proxy::class, $objects[0]);
278
        $this->assertInstanceOf(Proxy::class, $objects[1]);
279
        $this->assertInstanceOf(Proxy::class, $objects[2]);
280
        $this->assertSame('title', $objects[0]->withoutAutoRefresh()->getTitle());
281
        $this->assertSame('title', $objects[1]->withoutAutoRefresh()->getTitle());
282
        $this->assertSame('title', $objects[2]->withoutAutoRefresh()->getTitle());
283
    }
284
285
    /**
286
     * @test
287
     */
288
    public function creating_with_factory_attribute_persists_the_factory(): void
289
    {
290
        $registry = $this->createMock(ManagerRegistry::class);
291
        $registry
292
            ->expects($this->exactly(2))
293
            ->method('getManagerForClass')
294
            ->withConsecutive([Category::class], [Post::class])
295
            ->willReturn($this->createMock(ObjectManager::class))
296
        ;
297
298
        PersistenceManager::register($registry);
299
300
        $object = (new Factory(Post::class))->create([
301
            'title' => 'title',
302
            'body' => 'body',
303
            'category' => new Factory(Category::class),
304
        ]);
305
306
        $this->assertInstanceOf(Proxy::class, $object);
307
        $this->assertInstanceOf(Category::class, $object->withoutAutoRefresh()->getCategory());
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

307
        $this->assertInstanceOf(Category::class, $object->withoutAutoRefresh()->/** @scrutinizer ignore-call */ getCategory());
Loading history...
308
    }
309
310
    /**
311
     * @test
312
     */
313
    public function can_add_after_persist_events(): void
314
    {
315
        $registry = $this->createMock(ManagerRegistry::class);
316
        $registry
317
            ->expects($this->exactly(3)) // once for persisting, once for each afterPersist event
318
            ->method('getManagerForClass')
319
            ->with(Post::class)
320
            ->willReturn($this->createMock(ObjectManager::class))
321
        ;
322
323
        PersistenceManager::register($registry);
324
325
        $attributesArray = ['title' => 'title', 'body' => 'body'];
326
327
        $object = (new Factory(Post::class))
328
            ->afterPersist(function(Post $post, array $attributes) use ($attributesArray) {
329
                $this->assertSame($attributesArray, $attributes);
330
331
                $post->increaseViewCount();
332
            })
333
            ->afterPersist(function(Post $post, array $attributes) use ($attributesArray) {
334
                $this->assertSame($attributesArray, $attributes);
335
336
                $post->increaseViewCount();
337
            })
338
            ->create($attributesArray)
339
        ;
340
341
        $this->assertSame(2, $object->withoutAutoRefresh()->getViewCount());
0 ignored issues
show
Bug introduced by
The method getViewCount() 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

341
        $this->assertSame(2, $object->withoutAutoRefresh()->/** @scrutinizer ignore-call */ getViewCount());
Loading history...
342
    }
343
}
344