Skip to content

Commit 0428c04

Browse files
authored
Merge pull request #1 from jakzal/bugfix/multiple-kernels
Allow to use multiple kernels in a single test suite
2 parents fe459c1 + eda0376 commit 0428c04

File tree

8 files changed

+97
-49
lines changed

8 files changed

+97
-49
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ deptrac: vendor/bin/deptrac
3434
.PHONY: deptrac
3535

3636
infection: vendor/bin/infection vendor/bin/infection.pubkey
37-
phpdbg -qrr ./vendor/bin/infection --no-interaction --formatter=progress --min-msi=87 --min-covered-msi=89 --ansi
37+
phpdbg -qrr ./vendor/bin/infection --no-interaction --formatter=progress --min-msi=89 --min-covered-msi=91 --ansi
3838
.PHONY: infection
3939

4040
phpunit: vendor/bin/phpunit

depfile.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ ruleset:
7272
- TestCase
7373
- Symfony DependencyInjection
7474
Symfony TestCase:
75-
- Symfony Compiler
7675
- Psr Container
7776
- Symfony DependencyInjection
7877
- Symfony HttpKernel

src/Symfony/Compiler/ExposeServicesForTestsPass.php

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,51 +5,40 @@
55

66
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
77
use Symfony\Component\DependencyInjection\ContainerBuilder;
8+
use Symfony\Component\DependencyInjection\ContainerInterface;
89
use Symfony\Component\DependencyInjection\Reference;
910
use Symfony\Component\DependencyInjection\ServiceLocator;
1011
use Zalas\Injector\PHPUnit\Symfony\Compiler\Discovery\PropertyDiscovery;
1112
use Zalas\Injector\Service\Property;
1213

1314
class ExposeServicesForTestsPass implements CompilerPassInterface
1415
{
15-
const DEFAULT_SERVICE_LOCATOR_ID = 'app.test.service_locator';
16-
17-
/**
18-
* @var string
19-
*/
20-
private $serviceLocatorId;
21-
2216
/**
2317
* @var PropertyDiscovery
2418
*/
2519
private $propertyDiscovery;
2620

27-
public function __construct(string $serviceLocatorId = self::DEFAULT_SERVICE_LOCATOR_ID, ?PropertyDiscovery $propertyDiscovery = null)
21+
public function __construct(?PropertyDiscovery $propertyDiscovery = null)
2822
{
29-
$this->serviceLocatorId = $serviceLocatorId;
3023
$this->propertyDiscovery = $propertyDiscovery ?? new PropertyDiscovery();
3124
}
3225

3326
public function process(ContainerBuilder $container): void
3427
{
35-
$container->register($this->serviceLocatorId, ServiceLocator::class)
36-
->setPublic(true)
37-
->addTag('container.service_locator')
38-
->addArgument($this->discoverServices());
28+
foreach ($this->discoverServices() as $testClass => $references) {
29+
$container->register($testClass, ServiceLocator::class)
30+
->setPublic(true)
31+
->addTag('container.service_locator')
32+
->addArgument($references);
33+
}
3934
}
4035

4136
private function discoverServices(): array
4237
{
43-
return $this->flatMap(
44-
function (Property $property) {
45-
return [$property->getServiceId() => new Reference($property->getServiceId())];
46-
},
47-
$this->propertyDiscovery->run()
48-
);
49-
}
38+
return \array_reduce($this->propertyDiscovery->run(), function (array $services, Property $property) {
39+
$services[$property->getClassName()][$property->getServiceId()] = new Reference($property->getServiceId(), ContainerInterface::IGNORE_ON_INVALID_REFERENCE);
5040

51-
private function flatMap(callable $callback, array $collection): array
52-
{
53-
return \array_merge([], ...\array_map($callback, $collection));
41+
return $services;
42+
}, []);
5443
}
5544
}

src/Symfony/TestCase/SymfonyContainer.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
namespace Zalas\Injector\PHPUnit\Symfony\TestCase;
55

66
use Psr\Container\ContainerInterface;
7-
use Zalas\Injector\PHPUnit\Symfony\Compiler\ExposeServicesForTestsPass;
87

98
/**
109
* Provides a `ServiceContainerTestCase` implementation with the container created by the Symfony Kernel.
@@ -15,11 +14,6 @@ trait SymfonyContainer
1514

1615
public function createContainer(): ContainerInterface
1716
{
18-
return static::bootKernel()->getContainer()->get($this->getTestServiceLocatorId());
19-
}
20-
21-
protected function getTestServiceLocatorId(): string
22-
{
23-
return ExposeServicesForTestsPass::DEFAULT_SERVICE_LOCATOR_ID;
17+
return static::bootKernel()->getContainer()->get(__CLASS__);
2418
}
2519
}

tests/Symfony/Compiler/ExposeServicesForTestsPassTest.php

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@
77
use Prophecy\Prophecy\ObjectProphecy;
88
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
99
use Symfony\Component\DependencyInjection\ContainerBuilder;
10+
use Symfony\Component\DependencyInjection\ContainerInterface;
1011
use Symfony\Component\DependencyInjection\Reference;
1112
use Symfony\Component\DependencyInjection\ServiceLocator;
1213
use Zalas\Injector\PHPUnit\Symfony\Compiler\Discovery\PropertyDiscovery;
1314
use Zalas\Injector\PHPUnit\Symfony\Compiler\ExposeServicesForTestsPass;
1415
use Zalas\Injector\PHPUnit\Tests\Symfony\Compiler\Fixtures\Service1;
1516
use Zalas\Injector\PHPUnit\Tests\Symfony\Compiler\Fixtures\Service2;
1617
use Zalas\Injector\PHPUnit\Tests\Symfony\Compiler\Fixtures\TestCase1;
18+
use Zalas\Injector\PHPUnit\Tests\Symfony\Compiler\Fixtures\TestCase2;
1719
use Zalas\Injector\Service\Property;
1820

1921
class ExposeServicesForTestsPassTest extends TestCase
2022
{
21-
const SERVICE_LOCATOR_ID = 'app.test.service_locator';
22-
2323
/**
2424
* @var ExposeServicesForTestsPass
2525
*/
@@ -33,42 +33,49 @@ class ExposeServicesForTestsPassTest extends TestCase
3333
protected function setUp()
3434
{
3535
$this->discovery = $this->prophesize(PropertyDiscovery::class);
36-
$this->pass = new ExposeServicesForTestsPass(self::SERVICE_LOCATOR_ID, $this->discovery->reveal());
36+
$this->pass = new ExposeServicesForTestsPass($this->discovery->reveal());
3737
}
3838

3939
public function test_it_is_a_compiler_pass()
4040
{
4141
$this->assertInstanceOf(CompilerPassInterface::class, $this->pass);
4242
}
4343

44-
public function test_it_registers_a_service_locator_for_services_used_in_tests()
44+
public function test_it_registers_a_service_locator_for_each_test_case_requiring_service_injection()
4545
{
4646
$this->discovery->run()->willReturn([
4747
new Property(TestCase1::class, 'service1', Service1::class),
4848
new Property(TestCase1::class, 'service2', Service2::class),
49+
new Property(TestCase2::class, 'service2', Service2::class),
4950
]);
5051

5152
$container = new ContainerBuilder();
5253

5354
$this->pass->process($container);
5455

55-
$this->assertTrue($container->hasDefinition(self::SERVICE_LOCATOR_ID), 'The service locator is registered as a service.');
56-
$this->assertSame(ServiceLocator::class, $container->getDefinition(self::SERVICE_LOCATOR_ID)->getClass());
57-
$this->assertFalse($container->getDefinition(self::SERVICE_LOCATOR_ID)->isPrivate(), 'The service locator is registered as a public service.');
58-
$this->assertTrue($container->getDefinition(self::SERVICE_LOCATOR_ID)->isPublic(), 'The service locator is registered as a public service.');
59-
$this->assertTrue($container->getDefinition(self::SERVICE_LOCATOR_ID)->hasTag('container.service_locator'), 'The service locator is tagged.');
60-
$this->assertEquals([Service1::class => new Reference(Service1::class), Service2::class => new Reference(Service2::class)], $container->getDefinition(self::SERVICE_LOCATOR_ID)->getArgument(0));
56+
$this->assertTrue($container->hasDefinition(TestCase1::class), 'The first test case service locator is registered as a service.');
57+
$this->assertSame(ServiceLocator::class, $container->getDefinition(TestCase1::class)->getClass());
58+
$this->assertSame(ServiceLocator::class, $container->getDefinition(TestCase2::class)->getClass());
59+
$this->assertFalse($container->getDefinition(TestCase1::class)->isPrivate(), 'The first test case service locator is registered as a public service.');
60+
$this->assertTrue($container->getDefinition(TestCase1::class)->isPublic(), 'The first test case service locator is registered as a public service.');
61+
$this->assertTrue($container->getDefinition(TestCase1::class)->hasTag('container.service_locator'), 'The first case service locator is tagged.');
62+
$this->assertEquals([Service1::class => new Reference(Service1::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE), Service2::class => new Reference(Service2::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)], $container->getDefinition(TestCase1::class)->getArgument(0));
63+
$this->assertTrue($container->hasDefinition(TestCase2::class), 'The second test case service locator is registered as a service.');
64+
$this->assertFalse($container->getDefinition(TestCase2::class)->isPrivate(), 'The second test case service locator is registered as a public service.');
65+
$this->assertTrue($container->getDefinition(TestCase2::class)->isPublic(), 'The second test case service locator is registered as a public service.');
66+
$this->assertTrue($container->getDefinition(TestCase2::class)->hasTag('container.service_locator'), 'The second test case service locator is tagged.');
67+
$this->assertEquals([Service2::class => new Reference(Service2::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)], $container->getDefinition(TestCase2::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)->getArgument(0));
6168
}
6269

63-
public function test_it_registers_an_empty_service_locator_if_no_services_were_discovered()
70+
public function test_it_only_registers_a_service_locator_if_any_services_were_discovered()
6471
{
6572
$this->discovery->run()->willReturn([]);
6673

6774
$container = new ContainerBuilder();
6875

6976
$this->pass->process($container);
7077

71-
$this->assertTrue($container->hasDefinition(self::SERVICE_LOCATOR_ID), 'The service locator is registered as a service.');
72-
$this->assertEquals([], $container->getDefinition(self::SERVICE_LOCATOR_ID)->getArgument(0), 'No services were registered on the service locator.');
78+
$this->assertfalse($container->hasDefinition(TestCase1::class), 'The first test case service locator is not registered as a service.');
79+
$this->assertfalse($container->hasDefinition(TestCase2::class), 'The second test case service locator is not registered as a service.');
7380
}
7481
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures;
5+
6+
use Symfony\Component\Config\Loader\LoaderInterface;
7+
use Symfony\Component\DependencyInjection\ContainerBuilder;
8+
use Symfony\Component\HttpKernel\Kernel;
9+
use Zalas\Injector\PHPUnit\Symfony\Compiler\Discovery\ClassFinder;
10+
use Zalas\Injector\PHPUnit\Symfony\Compiler\Discovery\PropertyDiscovery;
11+
use Zalas\Injector\PHPUnit\Symfony\Compiler\ExposeServicesForTestsPass;
12+
13+
class AnotherTestKernel extends Kernel
14+
{
15+
public function registerBundles()
16+
{
17+
return [];
18+
}
19+
20+
public function getCacheDir()
21+
{
22+
return \sys_get_temp_dir().'/ZalasPHPUnitInjector2/cache/'.$this->environment;
23+
}
24+
25+
public function getLogDir()
26+
{
27+
return \sys_get_temp_dir().'/ZalasPHPUnitInjector2/logs';
28+
}
29+
30+
/**
31+
* Loads the container configuration.
32+
*/
33+
public function registerContainerConfiguration(LoaderInterface $loader)
34+
{
35+
$loader->load(function (ContainerBuilder $container) use ($loader) {
36+
$container->register(Service1::class, Service1::class);
37+
});
38+
}
39+
40+
protected function build(ContainerBuilder $container)
41+
{
42+
if ('test' === $this->getEnvironment()) {
43+
$container->addCompilerPass(
44+
new ExposeServicesForTestsPass(
45+
new PropertyDiscovery(new ClassFinder(__DIR__ . '/../'))
46+
)
47+
);
48+
}
49+
}
50+
}

tests/Symfony/TestCase/Fixtures/TestKernel.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ protected function build(ContainerBuilder $container)
4343
if ('test' === $this->getEnvironment()) {
4444
$container->addCompilerPass(
4545
new ExposeServicesForTestsPass(
46-
ExposeServicesForTestsPass::DEFAULT_SERVICE_LOCATOR_ID,
4746
new PropertyDiscovery(new ClassFinder(__DIR__ . '/../'))
4847
)
4948
);

tests/Symfony/TestCase/SymfonyContainerTest.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use Zalas\Injector\PHPUnit\TestCase\ServiceContainerTestCase;
1010
use Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\Service1;
1111
use Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\Service2;
12-
use Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\TestKernel;
1312

1413
class SymfonyContainerTest extends TestCase implements ServiceContainerTestCase
1514
{
@@ -27,6 +26,9 @@ class SymfonyContainerTest extends TestCase implements ServiceContainerTestCase
2726
*/
2827
private $service2;
2928

29+
/**
30+
* @env KERNEL_CLASS=Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\TestKernel
31+
*/
3032
public function test_it_initializes_the_container_by_booting_the_symfony_kernel()
3133
{
3234
$container = $this->createContainer();
@@ -38,8 +40,16 @@ public function test_it_initializes_the_container_by_booting_the_symfony_kernel(
3840
$this->assertInstanceOf(Service2::class, $container->get('foo.service2'));
3941
}
4042

41-
protected static function getKernelClass()
43+
/**
44+
* @env KERNEL_CLASS=Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\AnotherTestKernel
45+
*/
46+
public function test_it_ignores_missing_services_when_registering_the_service_locator()
4247
{
43-
return TestKernel::class;
48+
$container = $this->createContainer();
49+
50+
$this->assertInstanceOf(ServiceLocator::class, $container, 'Full container is not exposed.');
51+
$this->assertTrue($container->has(Service1::class), 'The private service is available in tests.');
52+
$this->assertFalse($container->has('foo.service2'), 'The private service is available in tests.');
53+
$this->assertInstanceOf(Service1::class, $container->get(Service1::class));
4454
}
4555
}

0 commit comments

Comments
 (0)