Skip to content

Commit a2a8d14

Browse files
authored
Merge pull request #4 from jakzal/symfony-4.1
Add support for the test service container from Symfony 4.1
2 parents 44a0444 + f2a3a4a commit a2a8d14

13 files changed

+268
-43
lines changed

Makefile

+1-1
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=89 --min-covered-msi=91 --ansi
37+
phpdbg -qrr ./vendor/bin/infection --no-interaction --formatter=progress --min-msi=91 --min-covered-msi=91 --only-covered --ansi
3838
.PHONY: infection
3939

4040
phpunit: vendor/bin/phpunit

README.md

+66-13
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,71 @@ The service is found by its type, or an id if it's given in the `@inject` tag.
7676
The `createServiceContainer` method would be usually provided by a base test case or a trait.
7777
In case of Symfony, such a trait is provided by this package (see the next section).
7878

79-
### Symfony
79+
### Symfony Test Container (Symfony >= 4.1)
8080

81-
The simplest way to inject services from a Symfony service container is to include
82-
the `Zalas\Injector\PHPUnit\Symfony\TestCase\SymfonyContainer` trait to get the default
83-
`Zalas\Injector\PHPUnit\TestListener\ServiceContainerTestCase` implementation:
81+
The `Zalas\Injector\PHPUnit\Symfony\TestCase\SymfonyTestContainer` trait provides
82+
access to the test container ([introduced in Symfony 4.1](https://symfony.com/blog/new-in-symfony-4-1-simpler-service-testing)).
83+
Including the trait in a test case implementing the `ServiceContainerTestCase` will make that services are injected
84+
into annotated properties:
85+
86+
```php
87+
use PHPUnit\Framework\TestCase;
88+
use Psr\Log\LoggerInterface;
89+
use Symfony\Component\Serializer\SerializerInterface;
90+
use Zalas\Injector\PHPUnit\Symfony\TestCase\SymfonyTestContainer;
91+
use Zalas\Injector\PHPUnit\TestCase\ServiceContainerTestCase;
92+
93+
class ServiceInjectorTest extends TestCase implements ServiceContainerTestCase
94+
{
95+
use SymfonyTestContainer;
96+
97+
/**
98+
* @var SerializerInterface
99+
* @inject
100+
*/
101+
private $serializer;
102+
103+
/**
104+
* @var LoggerInterface
105+
* @inject logger
106+
*/
107+
private $logger;
108+
109+
public function testThatServicesAreInjected()
110+
{
111+
$this->assertInstanceOf(SerializerInterface::class, $this->serializer, 'The service is injectd by its type');
112+
$this->assertInstanceOf(LoggerInterface::class, $this->logger, 'The service is injected by its id');
113+
}
114+
}
115+
```
116+
117+
Note that `test` needs to be set to `true` in your test environment configuration for the framework bundle:
118+
119+
```yaml
120+
framework:
121+
test: true
122+
```
123+
124+
Even though services are automatically made public by Symfony, the test container makes them available in your tests.
125+
Note that this only happens for private services that are actually used in your app (so are injected into
126+
a public service, i.e. a controller). If a service is not injected anywhere, it's removed by the container compiler.
127+
128+
The kernel used to bootstrap the container is created in a similar way to the `KernelTestCase` known from the FrameworkBundle.
129+
Similar environment variables are supported:
130+
131+
* `KERNEL_CLASS` *required* - kernel class to instantiate to create the service container
132+
* `APP_ENV` default: test - kernel environment
133+
* `APP_DEBUG` default: false - kernel debug flag
134+
135+
These could for example be configured in `phpunit.xml`, or via [global variables](https://github.com/jakzal/phpunit-globals).
136+
137+
### Symfony Container (Symfony 3.4 & 4.0)
138+
139+
The `Zalas\Injector\PHPUnit\Symfony\TestCase\SymfonyContainer` trait gives access to the full Symfony Container
140+
and can be used with any Symfony version.
141+
Opposed to the Test Container approach for Symfony 4.1, this version provides access to each service even if it's
142+
not used by your application anywhere and would normally be removed by the compiler.
143+
This should be treated as a limitation rather than a feature.
84144

85145
```php
86146
use PHPUnit\Framework\TestCase;
@@ -113,8 +173,8 @@ class ServiceInjectorTest extends TestCase implements ServiceContainerTestCase
113173
}
114174
```
115175

116-
To make this work the `Zalas\Injector\PHPUnit\Symfony\Compiler\ExposeServicesForTestsPass` needs to be
117-
registered in test environment:
176+
Since the test container is not available until Symfony 4.1,
177+
you'll also have to register the `Zalas\Injector\PHPUnit\Symfony\Compiler\ExposeServicesForTestsPass` compiler pass:
118178

119179
```php
120180
use Zalas\Injector\PHPUnit\Symfony\Compiler\ExposeServicesForTestsPass;
@@ -134,13 +194,6 @@ class Kernel extends BaseKernel
134194

135195
The compiler pass makes sure that even private services are available to be used in tests.
136196

137-
The kernel is created in a similar way to the `KernelTestCase` known from the FrameworkBundle.
138-
The same environment variables are supported:
139-
140-
* `KERNEL_CLASS` *required* - kernel class to instantiate to create the service container
141-
* `APP_ENV` default: test - kernel environment
142-
* `APP_DEBUG` default: false - kernel debug flag
143-
144197
## Contributing
145198

146199
Please read the [Contributing guide](CONTRIBUTING.md) to learn about contributing to this project.

composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"symfony/config": "^3.4 || ^4.0",
1313
"symfony/dependency-injection": "^3.4 || ^4.0",
1414
"symfony/http-kernel": "^3.4 || ^4.0",
15-
"zalas/phpunit-globals": "^1.0"
15+
"zalas/phpunit-globals": "^1.0",
16+
"symfony/framework-bundle": "^3.4||^4.0"
1617
},
1718
"autoload": {
1819
"psr-4": {

src/Symfony/Compiler/ExposeServicesForTestsPass.php

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
use Zalas\Injector\PHPUnit\Symfony\Compiler\Discovery\PropertyDiscovery;
1313
use Zalas\Injector\Service\Property;
1414

15+
/**
16+
* Looks for `@inject` annotations to register services in test service locators and make them available in tests.
17+
*/
1518
class ExposeServicesForTestsPass implements CompilerPassInterface
1619
{
1720
/**

src/Symfony/TestCase/SymfonyContainer.php

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
/**
99
* Provides a `ServiceContainerTestCase` implementation with the container created by the Symfony Kernel.
10+
*
11+
* Relies on `ExposeServicesForTestsPass` compiler pass being registered.
1012
*/
1113
trait SymfonyContainer
1214
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Zalas\Injector\PHPUnit\Symfony\TestCase;
5+
6+
use Psr\Container\ContainerInterface;
7+
8+
/**
9+
* Provides a `ServiceContainerTestCase` implementation with the test container from the Symfony FrameworkBundle.
10+
*
11+
* `framework.test` needs to be set to `true` in order for the `test.service_container` to be available.
12+
*/
13+
trait SymfonyTestContainer
14+
{
15+
use SymfonyKernel;
16+
17+
public function createContainer(): ContainerInterface
18+
{
19+
return static::bootKernel()->getContainer()->get('test.service_container');
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\FrameworkBundle;
5+
6+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
7+
use Symfony\Component\Config\Loader\LoaderInterface;
8+
use Symfony\Component\DependencyInjection\ContainerBuilder;
9+
use Symfony\Component\DependencyInjection\Reference;
10+
use Symfony\Component\HttpKernel\Kernel;
11+
use Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\Service1;
12+
13+
class AnotherTestKernel extends Kernel
14+
{
15+
public function registerBundles()
16+
{
17+
return [
18+
new FrameworkBundle(),
19+
];
20+
}
21+
22+
public function getCacheDir()
23+
{
24+
return \sys_get_temp_dir().'/ZalasPHPUnitInjector/FrameworkBundle/AnotherTestKernel/cache/'.$this->environment;
25+
}
26+
27+
public function getLogDir()
28+
{
29+
return \sys_get_temp_dir().'/ZalasPHPUnitInjector/FrameworkBundle/AnotherTestKernel/logs';
30+
}
31+
32+
public function registerContainerConfiguration(LoaderInterface $loader)
33+
{
34+
$loader->load(function (ContainerBuilder $container) use ($loader) {
35+
$container->register(Service1::class, Service1::class);
36+
37+
$container->register('public_service', \stdClass::class)
38+
->setPublic(true)
39+
->setArguments([
40+
new Reference(Service1::class)
41+
]);
42+
43+
$container->loadFromExtension('framework', ['test' => true]);
44+
$container->setParameter('kernel.secret', '$ecre7');
45+
});
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\FrameworkBundle;
5+
6+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
7+
use Symfony\Component\Config\Loader\LoaderInterface;
8+
use Symfony\Component\DependencyInjection\ContainerBuilder;
9+
use Symfony\Component\DependencyInjection\Reference;
10+
use Symfony\Component\HttpKernel\Kernel;
11+
use Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\Service1;
12+
use Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\Service2;
13+
14+
class TestKernel extends Kernel
15+
{
16+
public function registerBundles()
17+
{
18+
return [
19+
new FrameworkBundle(),
20+
];
21+
}
22+
23+
public function getCacheDir()
24+
{
25+
return \sys_get_temp_dir().'/ZalasPHPUnitInjector/FrameworkBundle/TestKernel/cache/'.$this->environment;
26+
}
27+
28+
public function getLogDir()
29+
{
30+
return \sys_get_temp_dir().'/ZalasPHPUnitInjector/FrameworkBundle/TestKernel/logs';
31+
}
32+
33+
public function registerContainerConfiguration(LoaderInterface $loader)
34+
{
35+
$loader->load(function (ContainerBuilder $container) use ($loader) {
36+
$container->register(Service1::class, Service1::class);
37+
$container->register('foo.service2', Service2::class);
38+
$container->register('public_service', \stdClass::class)
39+
->setPublic(true)
40+
->setArguments([
41+
new Reference(Service1::class),
42+
new Reference('foo.service2')
43+
]);
44+
45+
$container->loadFromExtension('framework', ['test' => true]);
46+
$container->setParameter('kernel.secret', '$ecre7');
47+
});
48+
}
49+
}

tests/Symfony/TestCase/Fixtures/AnotherTestKernel.php tests/Symfony/TestCase/Fixtures/NoFrameworkBundle/AnotherTestKernel.php

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
<?php
22
declare(strict_types=1);
33

4-
namespace Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures;
4+
namespace Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\NoFrameworkBundle;
55

66
use Symfony\Component\Config\Loader\LoaderInterface;
77
use Symfony\Component\DependencyInjection\ContainerBuilder;
8+
use Symfony\Component\DependencyInjection\Reference;
89
use Symfony\Component\HttpKernel\Kernel;
910
use Zalas\Injector\PHPUnit\Symfony\Compiler\Discovery\ClassFinder;
1011
use Zalas\Injector\PHPUnit\Symfony\Compiler\Discovery\PropertyDiscovery;
1112
use Zalas\Injector\PHPUnit\Symfony\Compiler\ExposeServicesForTestsPass;
13+
use Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\Service1;
1214

1315
class AnotherTestKernel extends Kernel
1416
{
@@ -19,17 +21,14 @@ public function registerBundles()
1921

2022
public function getCacheDir()
2123
{
22-
return \sys_get_temp_dir().'/ZalasPHPUnitInjector2/cache/'.$this->environment;
24+
return \sys_get_temp_dir().'/ZalasPHPUnitInjector/NoFrameworkBundle/AnotherTestKernel/cache/'.$this->environment;
2325
}
2426

2527
public function getLogDir()
2628
{
27-
return \sys_get_temp_dir().'/ZalasPHPUnitInjector2/logs';
29+
return \sys_get_temp_dir().'/ZalasPHPUnitInjector/NoFrameworkBundle/AnotherTestKernel/logs';
2830
}
2931

30-
/**
31-
* Loads the container configuration.
32-
*/
3332
public function registerContainerConfiguration(LoaderInterface $loader)
3433
{
3534
$loader->load(function (ContainerBuilder $container) use ($loader) {
@@ -42,7 +41,7 @@ protected function build(ContainerBuilder $container)
4241
if ('test' === $this->getEnvironment()) {
4342
$container->addCompilerPass(
4443
new ExposeServicesForTestsPass(
45-
new PropertyDiscovery(new ClassFinder(__DIR__ . '/../'))
44+
new PropertyDiscovery(new ClassFinder(__DIR__ . '/../../'))
4645
)
4746
);
4847
}

tests/Symfony/TestCase/Fixtures/TestKernel.php tests/Symfony/TestCase/Fixtures/NoFrameworkBundle/TestKernel.php

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
<?php
22
declare(strict_types=1);
33

4-
namespace Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures;
4+
namespace Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\NoFrameworkBundle;
55

66
use Symfony\Component\Config\Loader\LoaderInterface;
77
use Symfony\Component\DependencyInjection\ContainerBuilder;
8+
use Symfony\Component\DependencyInjection\Reference;
89
use Symfony\Component\HttpKernel\Kernel;
910
use Zalas\Injector\PHPUnit\Symfony\Compiler\Discovery\ClassFinder;
1011
use Zalas\Injector\PHPUnit\Symfony\Compiler\Discovery\PropertyDiscovery;
1112
use Zalas\Injector\PHPUnit\Symfony\Compiler\ExposeServicesForTestsPass;
13+
use Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\Service1;
14+
use Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\Service2;
1215

1316
class TestKernel extends Kernel
1417
{
@@ -19,17 +22,14 @@ public function registerBundles()
1922

2023
public function getCacheDir()
2124
{
22-
return \sys_get_temp_dir().'/ZalasPHPUnitInjector/cache/'.$this->environment;
25+
return \sys_get_temp_dir().'/ZalasPHPUnitInjector/NoFrameworkBundle/TestKernel/cache/'.$this->environment;
2326
}
2427

2528
public function getLogDir()
2629
{
27-
return \sys_get_temp_dir().'/ZalasPHPUnitInjector/logs';
30+
return \sys_get_temp_dir().'/ZalasPHPUnitInjector/NoFrameworkBundle/TestKernel/logs';
2831
}
2932

30-
/**
31-
* Loads the container configuration.
32-
*/
3333
public function registerContainerConfiguration(LoaderInterface $loader)
3434
{
3535
$loader->load(function (ContainerBuilder $container) use ($loader) {
@@ -43,7 +43,7 @@ protected function build(ContainerBuilder $container)
4343
if ('test' === $this->getEnvironment()) {
4444
$container->addCompilerPass(
4545
new ExposeServicesForTestsPass(
46-
new PropertyDiscovery(new ClassFinder(__DIR__ . '/../'))
46+
new PropertyDiscovery(new ClassFinder(__DIR__ . '/../../'))
4747
)
4848
);
4949
}

tests/Symfony/TestCase/SymfonyContainerTest.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class SymfonyContainerTest extends TestCase implements ServiceContainerTestCase
2727
private $service2;
2828

2929
/**
30-
* @env KERNEL_CLASS=Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\TestKernel
30+
* @env KERNEL_CLASS=Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\NoFrameworkBundle\TestKernel
3131
*/
3232
public function test_it_initializes_the_container_by_booting_the_symfony_kernel()
3333
{
@@ -41,15 +41,15 @@ public function test_it_initializes_the_container_by_booting_the_symfony_kernel(
4141
}
4242

4343
/**
44-
* @env KERNEL_CLASS=Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\AnotherTestKernel
44+
* @env KERNEL_CLASS=Zalas\Injector\PHPUnit\Tests\Symfony\TestCase\Fixtures\NoFrameworkBundle\AnotherTestKernel
4545
*/
4646
public function test_it_ignores_missing_services_when_registering_the_service_locator()
4747
{
4848
$container = $this->createContainer();
4949

5050
$this->assertInstanceOf(ServiceLocator::class, $container, 'Full container is not exposed.');
5151
$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.');
52+
$this->assertFalse($container->has('foo.service2'), 'The private service is not available in tests.');
5353
$this->assertInstanceOf(Service1::class, $container->get(Service1::class));
5454
}
5555
}

0 commit comments

Comments
 (0)