Completed
Push — fix-2494 ( 3153ee...40d9bb )
by Sam
13:43 queued 06:38
created

InjectorTest::testInjectUsingSetter()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 33
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 20
nc 1
nop 0
dl 0
loc 33
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Core\Tests\Injector;
4
5
use InvalidArgumentException;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Core\Injector\Factory;
8
use SilverStripe\Core\Injector\Injector;
9
use SilverStripe\Core\Injector\InjectorNotFoundException;
10
use SilverStripe\Core\Injector\SilverStripeServiceConfigurationLocator;
11
use SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService;
12
use SilverStripe\Core\Tests\Injector\AopProxyServiceTest\SampleService;
13
use SilverStripe\Core\Tests\Injector\InjectorTest\CircularOne;
14
use SilverStripe\Core\Tests\Injector\InjectorTest\CircularTwo;
15
use SilverStripe\Core\Tests\Injector\InjectorTest\ConstructableObject;
16
use SilverStripe\Core\Tests\Injector\InjectorTest\DummyRequirements;
17
use SilverStripe\Core\Tests\Injector\InjectorTest\MyChildClass;
18
use SilverStripe\Core\Tests\Injector\InjectorTest\MyParentClass;
19
use SilverStripe\Core\Tests\Injector\InjectorTest\NeedsBothCirculars;
20
use SilverStripe\Core\Tests\Injector\InjectorTest\NewRequirementsBackend;
21
use SilverStripe\Core\Tests\Injector\InjectorTest\OriginalRequirementsBackend;
22
use SilverStripe\Core\Tests\Injector\InjectorTest\OtherTestObject;
23
use SilverStripe\Core\Tests\Injector\InjectorTest\TestObject;
24
use SilverStripe\Core\Tests\Injector\InjectorTest\TestSetterInjections;
25
use SilverStripe\Core\Tests\Injector\InjectorTest\TestStaticInjections;
26
use SilverStripe\Dev\SapphireTest;
27
use stdClass;
28
29
define('TEST_SERVICES', __DIR__ . '/AopProxyServiceTest');
30
31
/**
32
 * Tests for the dependency injector
33
 *
34
 * Note that these are SS conversions of the existing Simpletest unit tests
35
 *
36
 * @author      [email protected]
37
 * @license     BSD License http://silverstripe.org/bsd-license/
38
 * @skipUpgrade
39
 */
40
class InjectorTest extends SapphireTest
41
{
42
43
    protected $nestingLevel = 0;
44
45
    protected function setUp()
46
    {
47
        parent::setUp();
48
49
        $this->nestingLevel = 0;
50
    }
51
52
    protected function tearDown()
53
    {
54
55
        while ($this->nestingLevel > 0) {
56
            $this->nestingLevel--;
57
            Config::unnest();
58
        }
59
60
        parent::tearDown();
61
    }
62
63
    public function testCorrectlyInitialised()
64
    {
65
        $injector = Injector::inst();
66
        $this->assertTrue(
67
            $injector->getConfigLocator() instanceof SilverStripeServiceConfigurationLocator,
68
            'Failure most likely because the injector has been referenced BEFORE being initialised in Core.php'
69
        );
70
    }
71
72 View Code Duplication
    public function testBasicInjector()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
73
    {
74
        $injector = new Injector();
75
        $injector->setAutoScanProperties(true);
76
        $config = array(
77
            'SampleService' => array(
78
                'src' => TEST_SERVICES . '/SampleService.php',
79
                'class' => SampleService::class,
80
            )
81
        );
82
83
        $injector->load($config);
84
85
86
        $this->assertFalse($injector->has('UnknownService'));
87
        $this->assertNull($injector->getServiceName('UnknownService'));
88
89
        $this->assertTrue($injector->has('SampleService'));
90
        $this->assertEquals(
91
            'SampleService',
92
            $injector->getServiceName('SampleService')
93
        );
94
95
        $myObject = new TestObject();
96
        $injector->inject($myObject);
97
98
        $this->assertInstanceOf(
99
            SampleService::class,
100
            $myObject->sampleService
101
        );
102
    }
103
104
    public function testConfiguredInjector()
105
    {
106
        $injector = new Injector();
107
        $services = array(
108
            'AnotherService' => array(
109
                'class' => AnotherService::class,
110
                'src' => TEST_SERVICES . '/AnotherService.php',
111
                'properties' => array('config_property' => 'Value'),
112
            ),
113
            'SampleService' => array(
114
                'class' => SampleService::class,
115
                'src' => TEST_SERVICES . '/SampleService.php',
116
            )
117
        );
118
119
        $injector->load($services);
120
        $this->assertTrue($injector->has('SampleService'));
121
        $this->assertEquals(
122
            'SampleService',
123
            $injector->getServiceName('SampleService')
124
        );
125
        // We expect a false because the AnotherService::class is actually
126
        // just a replacement of the SilverStripe\Core\Tests\Injector\AopProxyServiceTest\SampleService
127
        $this->assertTrue($injector->has('SampleService'));
128
        $this->assertEquals(
129
            'AnotherService',
130
            $injector->getServiceName('AnotherService')
131
        );
132
133
        $item = $injector->get('AnotherService');
134
135
        $this->assertEquals('Value', $item->config_property);
136
    }
137
138
    public function testIdToNameMap()
139
    {
140
        $injector = new Injector();
141
        $services = array(
142
            'FirstId' => AnotherService::class,
143
            'SecondId' => SampleService::class,
144
        );
145
146
        $injector->load($services);
147
148
        $this->assertTrue($injector->has('FirstId'));
149
        $this->assertEquals($injector->getServiceName('FirstId'), 'FirstId');
150
151
        $this->assertTrue($injector->has('SecondId'));
152
        $this->assertEquals($injector->getServiceName('SecondId'), 'SecondId');
153
154
        $this->assertTrue($injector->get('FirstId') instanceof AnotherService);
155
        $this->assertTrue($injector->get('SecondId') instanceof SampleService);
156
    }
157
158
    public function testReplaceService()
159
    {
160
        $injector = new Injector();
161
        $injector->setAutoScanProperties(true);
162
163
        $config = array(
164
            'SampleService' => array(
165
                'src' => TEST_SERVICES . '/SampleService.php',
166
                'class' => SampleService::class,
167
            )
168
        );
169
170
        // load
171
        $injector->load($config);
172
173
        // inject
174
        $myObject = new TestObject();
175
        $injector->inject($myObject);
176
177
        $this->assertInstanceOf(
178
            SampleService::class,
179
            $myObject->sampleService
180
        );
181
182
        // also tests that ID can be the key in the array
183
        $config = array(
184
            'SampleService' => array(
185
                'src' => TEST_SERVICES . '/AnotherService.php',
186
                'class' => AnotherService::class,
187
            )
188
        );
189
        // , 'id' => SampleService::class));
190
        // load
191
        $injector->load($config);
192
193
        $injector->inject($myObject);
194
        $this->assertInstanceOf(
195
            AnotherService::class,
196
            $myObject->sampleService
197
        );
198
    }
199
200
    public function testUpdateSpec()
201
    {
202
        $injector = new Injector();
203
        $services = array(
204
            AnotherService::class => array(
205
                'src' => TEST_SERVICES . '/AnotherService.php',
206
                'properties' => array(
207
                    'filters' => array(
208
                        'One',
209
                        'Two',
210
                    )
211
                ),
212
            )
213
        );
214
215
        $injector->load($services);
216
217
        $injector->updateSpec(AnotherService::class, 'filters', 'Three');
218
        $another = $injector->get(AnotherService::class);
219
220
        $this->assertEquals(3, count($another->filters));
221
        $this->assertEquals('Three', $another->filters[2]);
222
    }
223
224
    public function testConstantUsage()
225
    {
226
        $injector = new Injector();
227
        $services = array(
228
            AnotherService::class => array(
229
                'properties' => array(
230
                    'filters' => array(
231
                        '`BASE_PATH`',
232
                        '`TEMP_FOLDER`',
233
                        '`NOT_DEFINED`',
234
                        'THIRDPARTY_DIR' // Not back-tick escaped
235
                    )
236
                ),
237
            )
238
        );
239
240
        $injector->load($services);
241
        $another = $injector->get(AnotherService::class);
242
        $this->assertEquals(
243
            [
244
                BASE_PATH,
245
                TEMP_FOLDER,
246
                null,
247
                'THIRDPARTY_DIR',
248
            ],
249
            $another->filters
250
        );
251
    }
252
253 View Code Duplication
    public function testAutoSetInjector()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
254
    {
255
        $injector = new Injector();
256
        $injector->setAutoScanProperties(true);
257
        $injector->addAutoProperty('auto', 'somevalue');
0 ignored issues
show
Documentation introduced by
'somevalue' is of type string, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
258
        $config = array(
259
            'SampleService' => array(
260
                'src' => TEST_SERVICES . '/SampleService.php',
261
                'class' => SampleService::class
262
            )
263
        );
264
        $injector->load($config);
265
266
        $this->assertTrue($injector->has('SampleService'));
267
        $this->assertEquals(
268
            'SampleService',
269
            $injector->getServiceName('SampleService')
270
        );
271
        // We expect a false because the AnotherService::class is actually
272
        // just a replacement of the SilverStripe\Core\Tests\Injector\AopProxyServiceTest\SampleService
273
274
        $myObject = new InjectorTest\TestObject();
275
276
        $injector->inject($myObject);
277
278
        $this->assertInstanceOf(
279
            SampleService::class,
280
            $myObject->sampleService
281
        );
282
        $this->assertEquals($myObject->auto, 'somevalue');
0 ignored issues
show
Bug introduced by
The property auto does not seem to exist in SilverStripe\Core\Tests\...InjectorTest\TestObject.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
283
    }
284
285 View Code Duplication
    public function testSettingSpecificProperty()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
286
    {
287
        $injector = new Injector();
288
        $config = array(AnotherService::class);
289
        $injector->load($config);
290
        $injector->setInjectMapping(TestObject::class, 'sampleService', AnotherService::class);
291
        $testObject = $injector->get(TestObject::class);
292
293
        $this->assertInstanceOf(
294
            AnotherService::class,
295
            $testObject->sampleService
296
        );
297
    }
298
299 View Code Duplication
    public function testSettingSpecificMethod()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
300
    {
301
        $injector = new Injector();
302
        $config = array(AnotherService::class);
303
        $injector->load($config);
304
        $injector->setInjectMapping(TestObject::class, 'setSomething', AnotherService::class, 'method');
305
306
        $testObject = $injector->get(TestObject::class);
307
308
        $this->assertInstanceOf(
309
            AnotherService::class,
310
            $testObject->sampleService
311
        );
312
    }
313
314
    public function testInjectingScopedService()
315
    {
316
        $injector = new Injector();
317
318
        $config = array(
319
            AnotherService::class,
320
            'SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService.DottedChild'   => SampleService::class,
321
        );
322
323
        $injector->load($config);
324
325
        $service = $injector->get('SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService.DottedChild');
326
        $this->assertInstanceOf(SampleService::class, $service);
327
328
        $service = $injector->get('SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService.Subset');
329
        $this->assertInstanceOf(AnotherService::class, $service);
330
331
        $injector->setInjectMapping(TestObject::class, 'sampleService', 'SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService.Geronimo');
332
        $testObject = $injector->create(TestObject::class);
333
        $this->assertEquals(get_class($testObject->sampleService), AnotherService::class);
334
335
        $injector->setInjectMapping(TestObject::class, 'sampleService', 'SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService.DottedChild.AnotherDown');
336
        $testObject = $injector->create(TestObject::class);
337
        $this->assertEquals(get_class($testObject->sampleService), SampleService::class);
338
    }
339
340
    public function testInjectUsingConstructor()
341
    {
342
        $injector = new Injector();
343
        $config = array(
344
            'SampleService' => array(
345
                'src' => TEST_SERVICES . '/SampleService.php',
346
                'class' => SampleService::class,
347
                'constructor' => array(
348
                    'val1',
349
                    'val2',
350
                )
351
            )
352
        );
353
354
        $injector->load($config);
355
        $sample = $injector->get('SampleService');
356
        $this->assertEquals($sample->constructorVarOne, 'val1');
357
        $this->assertEquals($sample->constructorVarTwo, 'val2');
358
359
        $injector = new Injector();
360
        $config = array(
361
            'AnotherService' => AnotherService::class,
362
            'SampleService' => array(
363
                'src' => TEST_SERVICES . '/SampleService.php',
364
                'class' => SampleService::class,
365
                'constructor' => array(
366
                    'val1',
367
                    '%$AnotherService',
368
                )
369
            )
370
        );
371
372
        $injector->load($config);
373
        $sample = $injector->get('SampleService');
374
        $this->assertEquals($sample->constructorVarOne, 'val1');
375
        $this->assertInstanceOf(
376
            AnotherService::class,
377
            $sample->constructorVarTwo
378
        );
379
380
        $injector = new Injector();
381
        $config = array(
382
            'SampleService' => array(
383
                'src' => TEST_SERVICES . '/SampleService.php',
384
                'class' => SampleService::class,
385
                'constructor' => array(
386
                    'val1',
387
                    'val2',
388
                )
389
            )
390
        );
391
392
        $injector->load($config);
393
        $sample = $injector->get('SampleService');
394
        $this->assertEquals($sample->constructorVarOne, 'val1');
395
        $this->assertEquals($sample->constructorVarTwo, 'val2');
396
397
        // test constructors on prototype
398
        $injector = new Injector();
399
        $config = array(
400
            'SampleService' => array(
401
                'type'  => 'prototype',
402
                'src' => TEST_SERVICES . '/SampleService.php',
403
                'class' => SampleService::class,
404
                'constructor' => array(
405
                    'val1',
406
                    'val2',
407
                )
408
            )
409
        );
410
411
        $injector->load($config);
412
        $sample = $injector->get('SampleService');
413
        $this->assertEquals($sample->constructorVarOne, 'val1');
414
        $this->assertEquals($sample->constructorVarTwo, 'val2');
415
416
        $again = $injector->get('SampleService');
417
        $this->assertFalse($sample === $again);
418
419
        $this->assertEquals($sample->constructorVarOne, 'val1');
420
        $this->assertEquals($sample->constructorVarTwo, 'val2');
421
    }
422
423
    public function testInjectUsingSetter()
424
    {
425
        $injector = new Injector();
426
        $injector->setAutoScanProperties(true);
427
        $config = array(
428
            'SampleService' => array(
429
                'src' => TEST_SERVICES . '/SampleService.php',
430
                'class' => SampleService::class,
431
            )
432
        );
433
434
        $injector->load($config);
435
        $this->assertTrue($injector->has('SampleService'));
436
        $this->assertEquals('SampleService', $injector->getServiceName('SampleService'));
437
438
        $myObject = new InjectorTest\OtherTestObject();
439
        $injector->inject($myObject);
440
441
        $this->assertInstanceOf(
442
            SampleService::class,
443
            $myObject->s()
444
        );
445
446
        // and again because it goes down a different code path when setting things
447
        // based on the inject map
448
        $myObject = new InjectorTest\OtherTestObject();
449
        $injector->inject($myObject);
450
451
        $this->assertInstanceOf(
452
            SampleService::class,
453
            $myObject->s()
454
        );
455
    }
456
457
    // make sure we can just get any arbitrary object - it should be created for us
458
    public function testInstantiateAnObjectViaGet()
459
    {
460
        $injector = new Injector();
461
        $injector->setAutoScanProperties(true);
462
        $config = array(
463
            'SampleService' => array(
464
                'src' => TEST_SERVICES . '/SampleService.php',
465
                'class' => SampleService::class,
466
            )
467
        );
468
469
        $injector->load($config);
470
        $this->assertTrue($injector->has('SampleService'));
471
        $this->assertEquals('SampleService', $injector->getServiceName('SampleService'));
472
473
        $myObject = $injector->get(OtherTestObject::class);
474
        $this->assertInstanceOf(
475
            SampleService::class,
476
            $myObject->s()
477
        );
478
479
        // and again because it goes down a different code path when setting things
480
        // based on the inject map
481
        $myObject = $injector->get(OtherTestObject::class);
482
        $this->assertInstanceOf(SampleService::class, $myObject->s());
483
    }
484
485
    public function testCircularReference()
486
    {
487
        $services = array(
488
            'CircularOne' => CircularOne::class,
489
            'CircularTwo' => CircularTwo::class
490
        );
491
        $injector = new Injector($services);
492
        $injector->setAutoScanProperties(true);
493
494
        $obj = $injector->get(NeedsBothCirculars::class);
495
496
        $this->assertTrue($obj->circularOne instanceof InjectorTest\CircularOne);
497
        $this->assertTrue($obj->circularTwo instanceof InjectorTest\CircularTwo);
498
    }
499
500
    public function testPrototypeObjects()
501
    {
502
        $services = array(
503
            'CircularOne' => CircularOne::class,
504
            'CircularTwo' => CircularTwo::class,
505
            'NeedsBothCirculars' => array(
506
                'class' => NeedsBothCirculars::class,
507
                'type' => 'prototype'
508
            )
509
        );
510
        $injector = new Injector($services);
511
        $injector->setAutoScanProperties(true);
512
        $obj1 = $injector->get('NeedsBothCirculars');
513
        $obj2 = $injector->get('NeedsBothCirculars');
514
515
        // if this was the same object, then $obj1->var would now be two
516
        $obj1->var = 'one';
517
        $obj2->var = 'two';
518
519
        $this->assertTrue($obj1->circularOne instanceof InjectorTest\CircularOne);
520
        $this->assertTrue($obj1->circularTwo instanceof InjectorTest\CircularTwo);
521
522
        $this->assertEquals($obj1->circularOne, $obj2->circularOne);
523
        $this->assertNotEquals($obj1, $obj2);
524
    }
525
526
    public function testSimpleInstantiation()
527
    {
528
        $services = array(
529
            'CircularOne' => CircularOne::class,
530
            'CircularTwo' => CircularTwo::class
531
        );
532
        $injector = new Injector($services);
533
534
        // similar to the above, but explicitly instantiating this object here
535
        $obj1 = $injector->create(NeedsBothCirculars::class);
536
        $obj2 = $injector->create(NeedsBothCirculars::class);
537
538
        // if this was the same object, then $obj1->var would now be two
539
        $obj1->var = 'one';
540
        $obj2->var = 'two';
541
542
        $this->assertEquals($obj1->circularOne, $obj2->circularOne);
543
        $this->assertNotEquals($obj1, $obj2);
544
    }
545
546
    public function testCreateWithConstructor()
547
    {
548
        $injector = new Injector();
549
        $obj = $injector->create(CircularTwo::class, 'param');
550
        $this->assertEquals($obj->otherVar, 'param');
551
    }
552
553
    public function testSimpleSingleton()
554
    {
555
        $injector = new Injector();
556
557
        $one = $injector->create(CircularOne::class);
558
        $two = $injector->create(CircularOne::class);
559
560
        $this->assertFalse($one === $two);
561
562
        $one = $injector->get(CircularTwo::class);
563
        $two = $injector->get(CircularTwo::class);
564
565
        $this->assertTrue($one === $two);
566
    }
567
568
    public function testOverridePriority()
569
    {
570
        $injector = new Injector();
571
        $injector->setAutoScanProperties(true);
572
        $config = array(
573
            'SampleService' => array(
574
                'src' => TEST_SERVICES . '/SampleService.php',
575
                'class' => SampleService::class,
576
                'priority' => 10,
577
            )
578
        );
579
580
        // load
581
        $injector->load($config);
582
583
        // inject
584
        $myObject = new InjectorTest\TestObject();
585
        $injector->inject($myObject);
586
587
        $this->assertInstanceOf(SampleService::class, $myObject->sampleService);
588
589
        $config = array(
590
            array(
591
                'src' => TEST_SERVICES . '/AnotherService.php',
592
                'class' => AnotherService::class,
593
                'id' => 'SampleService',
594
                'priority' => 1,
595
            )
596
        );
597
        // load
598
        $injector->load($config);
599
600
        $injector->inject($myObject);
601
        $this->assertInstanceOf(
602
            SampleService::class,
603
            $myObject->sampleService
604
        );
605
    }
606
607
    /**
608
     * Specific test method to illustrate various ways of setting a requirements backend
609
     */
610
    public function testRequirementsSettingOptions()
611
    {
612
        $injector = new Injector();
613
        $config = array(
614
            OriginalRequirementsBackend::class,
615
            NewRequirementsBackend::class,
616
            DummyRequirements::class => array(
617
                'constructor' => array(
618
                    '%$'.OriginalRequirementsBackend::class
619
                )
620
            )
621
        );
622
623
        $injector->load($config);
624
625
        $requirements = $injector->get(DummyRequirements::class);
626
        $this->assertInstanceOf(
627
            OriginalRequirementsBackend::class,
628
            $requirements->backend
629
        );
630
631
        // just overriding the definition here
632
        $injector->load(
633
            array(
634
            DummyRequirements::class => array(
635
                'constructor' => array(
636
                    '%$'.NewRequirementsBackend::class
637
                )
638
            )
639
            )
640
        );
641
642
        // requirements should have been reinstantiated with the new bean setting
643
        $requirements = $injector->get(DummyRequirements::class);
644
        $this->assertInstanceOf(
645
            NewRequirementsBackend::class,
646
            $requirements->backend
647
        );
648
    }
649
650
    /**
651
     * disabled for now
652
     */
653 View Code Duplication
    public function testStaticInjections()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
654
    {
655
        $injector = new Injector();
656
        $config = array(
657
            NewRequirementsBackend::class,
658
        );
659
660
        $injector->load($config);
661
662
        $si = $injector->get(TestStaticInjections::class);
663
        $this->assertInstanceOf(
664
            NewRequirementsBackend::class,
665
            $si->backend
666
        );
667
    }
668
669 View Code Duplication
    public function testSetterInjections()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
670
    {
671
        $injector = new Injector();
672
        $config = array(
673
            NewRequirementsBackend::class,
674
        );
675
676
        $injector->load($config);
677
678
        $si = $injector->get(TestSetterInjections::class);
679
        $this->assertInstanceOf(
680
            NewRequirementsBackend::class,
681
            $si->getBackend()
682
        );
683
    }
684
685
    public function testCustomObjectCreator()
686
    {
687
        $injector = new Injector();
688
        $injector->setObjectCreator(new InjectorTest\SSObjectCreator($injector));
689
        $config = array(
690
            OriginalRequirementsBackend::class,
691
            DummyRequirements::class => array(
692
                'class' => DummyRequirements::class.'(\'%$'.OriginalRequirementsBackend::class.'\')'
693
            )
694
        );
695
        $injector->load($config);
696
697
        $requirements = $injector->get(DummyRequirements::class);
698
        $this->assertEquals(OriginalRequirementsBackend::class, get_class($requirements->backend));
699
    }
700
701
    public function testInheritedConfig()
702
    {
703
704
        // Test that child class does not automatically inherit config
705
        $injector = new Injector(array('locator' => SilverStripeServiceConfigurationLocator::class));
706
        Config::inst()->update(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
707
            Injector::class,
708
            MyParentClass::class,
709
            [
710
            'properties' => ['one' => 'the one'],
711
            'class' => MyParentClass::class,
712
            ]
713
        );
714
        $obj = $injector->get(MyParentClass::class);
715
        $this->assertInstanceOf(MyParentClass::class, $obj);
716
        $this->assertEquals($obj->one, 'the one');
717
718
        // Class isn't inherited and parent properties are ignored
719
        $obj = $injector->get(MyChildClass::class);
720
        $this->assertInstanceOf(MyChildClass::class, $obj);
721
        $this->assertNotEquals($obj->one, 'the one');
722
723
        // Set child class as alias
724
        $injector = new Injector(
725
            array(
726
            'locator' => SilverStripeServiceConfigurationLocator::class
727
            )
728
        );
729
        Config::inst()->update(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
730
            Injector::class,
731
            MyChildClass::class,
732
            '%$'.MyParentClass::class
733
        );
734
735
        // Class isn't inherited and parent properties are ignored
736
        $obj = $injector->get(MyChildClass::class);
737
        $this->assertInstanceOf(MyParentClass::class, $obj);
738
        $this->assertEquals($obj->one, 'the one');
739
    }
740
741
    public function testSameNamedSingeltonPrototype()
742
    {
743
        $injector = new Injector();
744
745
        // get a singleton object
746
        $object = $injector->get(NeedsBothCirculars::class);
747
        $object->var = 'One';
748
749
        $again = $injector->get(NeedsBothCirculars::class);
750
        $this->assertEquals($again->var, 'One');
751
752
        // create a NEW instance object
753
        $new = $injector->create(NeedsBothCirculars::class);
754
        $this->assertNull($new->var);
755
756
        // this will trigger a problem below
757
        $new->var = 'Two';
758
759
        $again = $injector->get(NeedsBothCirculars::class);
760
        $this->assertEquals($again->var, 'One');
761
    }
762
763
    public function testConvertServicePropertyOnCreate()
764
    {
765
        // make sure convert service property is not called on direct calls to create, only on configured
766
        // declarations to avoid un-needed function calls
767
        $injector = new Injector();
768
        $item = $injector->create(ConstructableObject::class, '%$'.TestObject::class);
769
        $this->assertEquals('%$'.TestObject::class, $item->property);
770
771
        // do it again but have test object configured as a constructor dependency
772
        $injector = new Injector();
773
        $config = array(
774
            ConstructableObject::class => array(
775
                'constructor' => array(
776
                    '%$'.TestObject::class
777
                )
778
            )
779
        );
780
781
        $injector->load($config);
782
        $item = $injector->get(ConstructableObject::class);
783
        $this->assertTrue($item->property instanceof InjectorTest\TestObject);
784
785
        // and with a configured object defining TestObject to be something else!
786
        $injector = new Injector(array('locator' => InjectorTest\InjectorTestConfigLocator::class));
787
        $config = array(
788
            ConstructableObject::class => array(
789
                'constructor' => array(
790
                    '%$'.TestObject::class
791
                )
792
            ),
793
        );
794
795
        $injector->load($config);
796
        $item = $injector->get(ConstructableObject::class);
797
        $this->assertTrue($item->property instanceof InjectorTest\ConstructableObject);
798
799
        $this->assertInstanceOf(OtherTestObject::class, $item->property->property);
800
    }
801
802
    public function testNamedServices()
803
    {
804
        $injector = new Injector();
805
        $service  = new stdClass();
806
807
        $injector->registerService($service, 'NamedService');
808
        $this->assertEquals($service, $injector->get('NamedService'));
809
    }
810
811
    public function testCreateConfiggedObjectWithCustomConstructorArgs()
812
    {
813
        // need to make sure that even if the config defines some constructor params,
814
        // that we take our passed in constructor args instead
815
        $injector = new Injector(array('locator' => InjectorTest\InjectorTestConfigLocator::class));
816
817
        $item = $injector->create('ConfigConstructor', 'othervalue');
818
        $this->assertEquals($item->property, 'othervalue');
819
    }
820
821
    /**
822
     * Tests creating a service with a custom factory.
823
     */
824
    public function testCustomFactory()
825
    {
826
        $injector = new Injector(
827
            array(
828
            'service' => array('factory' => 'factory', 'constructor' => array(1, 2, 3))
829
            )
830
        );
831
832
        $factory = $this->getMockBuilder(Factory::class)->getMock();
833
        $factory
834
            ->expects($this->once())
835
            ->method('create')
836
            ->with($this->equalTo('service'), $this->equalTo(array(1, 2, 3)))
837
            ->will(
838
                $this->returnCallback(
839
                    function ($args) {
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed.

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

Loading history...
840
                        return new InjectorTest\TestObject();
841
                    }
842
                )
843
            );
844
845
        $injector->registerService($factory, 'factory');
846
847
        $this->assertInstanceOf(TestObject::class, $injector->get('service'));
848
    }
849
850
    public function testMethods()
851
    {
852
        // do it again but have test object configured as a constructor dependency
853
        $injector = new Injector();
854
        $config = array(
855
            'A' => array(
856
                'class' => TestObject::class,
857
            ),
858
            'B' => array(
859
                'class' => TestObject::class,
860
            ),
861
            'TestService' => array(
862
                'class' => TestObject::class,
863
                'calls' => array(
864
                    array('myMethod', array('%$A')),
865
                    array('myMethod', array('%$B')),
866
                    array('noArgMethod')
867
                )
868
            )
869
        );
870
871
        $injector->load($config);
872
        $item = $injector->get('TestService');
873
        $this->assertTrue($item instanceof InjectorTest\TestObject);
874
        $this->assertEquals(
875
            array($injector->get('A'), $injector->get('B'), 'noArgMethod called'),
876
            $item->methodCalls
877
        );
878
    }
879
880
    /**
881
     * @expectedException InvalidArgumentException
882
     */
883 View Code Duplication
    public function testNonExistentMethods()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
884
    {
885
        $injector = new Injector();
886
        $config = array(
887
            'TestService' => array(
888
                'class' => TestObject::class,
889
                'calls' => array(
890
                    array('thisDoesntExist')
891
                )
892
            )
893
        );
894
895
        $injector->load($config);
896
        $item = $injector->get('TestService');
0 ignored issues
show
Unused Code introduced by
$item is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
897
    }
898
899
    /**
900
     * @expectedException InvalidArgumentException
901
     */
902 View Code Duplication
    public function testProtectedMethods()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
903
    {
904
        $injector = new Injector();
905
        $config = array(
906
            'TestService' => array(
907
                'class' => TestObject::class,
908
                'calls' => array(
909
                    array('protectedMethod')
910
                )
911
            )
912
        );
913
914
        $injector->load($config);
915
        $item = $injector->get('TestService');
0 ignored issues
show
Unused Code introduced by
$item is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
916
    }
917
918
    /**
919
     * @expectedException InvalidArgumentException
920
     */
921
    public function testTooManyArrayValues()
922
    {
923
        $injector = new Injector();
924
        $config = array(
925
            'TestService' => array(
926
                'class' => TestObject::class,
927
                'calls' => array(
928
                    array('method', array('args'), 'what is this?')
929
                )
930
            )
931
        );
932
933
        $injector->load($config);
934
        $item = $injector->get('TestService');
0 ignored issues
show
Unused Code introduced by
$item is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
935
    }
936
937
    /**
938
     * @expectedException \SilverStripe\Core\Injector\InjectorNotFoundException
939
     */
940
    public function testGetThrowsOnNotFound()
941
    {
942
        $injector = new Injector();
943
        $injector->get('UnknownService');
944
    }
945
946
    /**
947
     * Test nesting of injector
948
     */
949
    public function testNest()
950
    {
951
952
        // Outer nest to avoid interference with other
953
        Injector::nest();
954
        $this->nestingLevel++;
955
956
        // Test services
957
        $config = array(
958
            NewRequirementsBackend::class,
959
        );
960
        Injector::inst()->load($config);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method load() does only exist in the following implementations of said interface: SilverStripe\Core\Injector\Injector.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
961
        $si = Injector::inst()->get(TestStaticInjections::class);
962
        $this->assertInstanceOf(TestStaticInjections::class, $si);
963
        $this->assertInstanceOf(NewRequirementsBackend::class, $si->backend);
964
        $this->assertInstanceOf(MyParentClass::class, Injector::inst()->get(MyParentClass::class));
965
        $this->assertInstanceOf(MyChildClass::class, Injector::inst()->get(MyChildClass::class));
966
967
        // Test that nested injector values can be overridden
968
        Injector::nest();
969
        $this->nestingLevel++;
970
        Injector::inst()->unregisterAllObjects();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method unregisterAllObjects() does only exist in the following implementations of said interface: SilverStripe\Core\Injector\Injector.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
971
        $newsi = Injector::inst()->get(TestStaticInjections::class);
972
        $newsi->backend = new InjectorTest\OriginalRequirementsBackend();
973
        Injector::inst()->registerService($newsi, TestStaticInjections::class);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method registerService() does only exist in the following implementations of said interface: SilverStripe\Core\Injector\Injector.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
974
        Injector::inst()->registerService(new InjectorTest\MyChildClass(), MyParentClass::class);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method registerService() does only exist in the following implementations of said interface: SilverStripe\Core\Injector\Injector.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
975
976
        // Check that these overridden values are retrievable
977
        $si = Injector::inst()->get(TestStaticInjections::class);
978
        $this->assertInstanceOf(TestStaticInjections::class, $si);
979
        $this->assertInstanceOf(OriginalRequirementsBackend::class, $si->backend);
980
        $this->assertInstanceOf(MyParentClass::class, Injector::inst()->get(MyParentClass::class));
981
        $this->assertInstanceOf(MyParentClass::class, Injector::inst()->get(MyChildClass::class));
982
983
        // Test that unnesting restores expected behaviour
984
        Injector::unnest();
985
        $this->nestingLevel--;
986
        $si = Injector::inst()->get(TestStaticInjections::class);
987
        $this->assertInstanceOf(TestStaticInjections::class, $si);
988
        $this->assertInstanceOf(NewRequirementsBackend::class, $si->backend);
989
        $this->assertInstanceOf(MyParentClass::class, Injector::inst()->get(MyParentClass::class));
990
        $this->assertInstanceOf(MyChildClass::class, Injector::inst()->get(MyChildClass::class));
991
992
        // Test reset of cache
993
        Injector::inst()->unregisterAllObjects();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method unregisterAllObjects() does only exist in the following implementations of said interface: SilverStripe\Core\Injector\Injector.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
994
        $si = Injector::inst()->get(TestStaticInjections::class);
995
        $this->assertInstanceOf(TestStaticInjections::class, $si);
996
        $this->assertInstanceOf(NewRequirementsBackend::class, $si->backend);
997
        $this->assertInstanceOf(MyParentClass::class, Injector::inst()->get(MyParentClass::class));
998
        $this->assertInstanceOf(MyChildClass::class, Injector::inst()->get(MyChildClass::class));
999
1000
        // Return to nestingLevel 0
1001
        Injector::unnest();
1002
        $this->nestingLevel--;
1003
    }
1004
}
1005