Skip to content

Commit 183b91c

Browse files
committed
feature #40366 [FrameworkBundle] Add KernelTestCase::getContainer() (Nyholm)
This PR was squashed before being merged into the 5.3-dev branch. Discussion ---------- [FrameworkBundle] Add KernelTestCase::getContainer() | Q | A | ------------- | --- | Branch? | 5.x | Bug fix? | no | New feature? | yes | Deprecations? | yes | Tickets | | License | MIT | Doc PR | symfony/symfony-docs#14731 There are at least 3 ways to get the container in a test class: ```php class FooTest extends WebTestCase { public function testGetContainerA() { $kernel = self::bootKernel(); $container = $kernel->getContainer(); } public function testGetContainerB() { self::bootKernel(); $container = self::$container; } public function testGetContainerC() { $client = self::createClient(); $container = $client->getContainer(); } } ``` I suggest to add a fourth =) Basically, in tests you should always use the `test.service_container`. It is hard to remove A and C, but I can deprecate C and add a helper function. ```php class FooTest extends WebTestCase { public function testGetContainerTheOnlyWayYouShouldUse() { $container = $this->getContainer(); } } ``` This new way will also boot your kernel if it is not already booted. Commits ------- f4c9724 [FrameworkBundle] Add KernelTestCase::getContainer()
2 parents 5d72815 + f4c9724 commit 183b91c

21 files changed

+123
-40
lines changed

UPGRADE-5.3.md

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ FrameworkBundle
3535
* Deprecate the `session.storage` alias and `session.storage.*` services, use the `session.storage.factory` alias and `session.storage.factory.*` services instead
3636
* Deprecate the `framework.session.storage_id` configuration option, use the `framework.session.storage_factory_id` configuration option instead
3737
* Deprecate the `session` service and the `SessionInterface` alias, use the `\Symfony\Component\HttpFoundation\Request::getSession()` or the new `\Symfony\Component\HttpFoundation\RequestStack::getSession()` methods instead
38+
* Deprecate the `KernelTestCase::$container` property, use `KernelTestCase::getContainer()` instead
3839

3940
HttpFoundation
4041
--------------

UPGRADE-6.0.md

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ FrameworkBundle
7878
* The `form.factory`, `form.type.file`, `translator`, `security.csrf.token_manager`, `serializer`,
7979
`cache_clearer`, `filesystem` and `validator` services are now private.
8080
* Removed the `lock.RESOURCE_NAME` and `lock.RESOURCE_NAME.store` services and the `lock`, `LockInterface`, `lock.store` and `PersistingStoreInterface` aliases, use `lock.RESOURCE_NAME.factory`, `lock.factory` or `LockFactory` instead.
81+
* Remove the `KernelTestCase::$container` property, use `KernelTestCase::getContainer()` instead
8182

8283
HttpFoundation
8384
--------------

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ CHANGELOG
1616
* Add tag `assets.package` to register asset packages
1717
* Add support to use a PSR-6 compatible cache for Doctrine annotations
1818
* Deprecate all other values than "none", "php_array" and "file" for `framework.annotation.cache`
19+
* Add `KernelTestCase::getContainer()` as the best way to get a container in tests
1920

2021
5.2.0
2122
-----

src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php

+24
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\DependencyInjection\ContainerInterface;
16+
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
1617
use Symfony\Component\HttpKernel\KernelInterface;
1718
use Symfony\Contracts\Service\ResetInterface;
1819

@@ -34,6 +35,8 @@ abstract class KernelTestCase extends TestCase
3435

3536
/**
3637
* @var ContainerInterface
38+
*
39+
* @deprecated since Symfony 5.3, use static::getContainer() instead
3740
*/
3841
protected static $container;
3942

@@ -86,6 +89,27 @@ protected static function bootKernel(array $options = [])
8689
return static::$kernel;
8790
}
8891

92+
/**
93+
* Provides a dedicated test container with access to both public and private
94+
* services. The container will not include private services that has been
95+
* inlined or removed. Private services will be removed when they are not
96+
* used by other services.
97+
*
98+
* Using this method is the best way to get a container from your test code.
99+
*/
100+
protected static function getContainer(): ContainerInterface
101+
{
102+
if (!static::$booted) {
103+
static::bootKernel();
104+
}
105+
106+
try {
107+
return self::$kernelContainer->get('test.service_container');
108+
} catch (ServiceNotFoundException $e) {
109+
throw new \LogicException('Could not find service "test.service_container". Try updating the "framework.test" config to "true".', 0, $e);
110+
}
111+
}
112+
89113
/**
90114
* Creates a Kernel.
91115
*

src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php

+5-4
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,13 @@ public static function getMailerMessage(int $index = 0, string $transport = null
118118

119119
private static function getMessageMailerEvents(): MessageEvents
120120
{
121-
if (self::$container->has('mailer.message_logger_listener')) {
122-
return self::$container->get('mailer.message_logger_listener')->getEvents();
121+
$container = static::getContainer();
122+
if ($container->has('mailer.message_logger_listener')) {
123+
return $container->get('mailer.message_logger_listener')->getEvents();
123124
}
124125

125-
if (self::$container->has('mailer.logger_message_listener')) {
126-
return self::$container->get('mailer.logger_message_listener')->getEvents();
126+
if ($container->has('mailer.logger_message_listener')) {
127+
return $container->get('mailer.logger_message_listener')->getEvents();
127128
}
128129

129130
static::fail('A client must have Mailer enabled to make email assertions. Did you forget to require symfony/mailer?');

src/Symfony/Bundle/FrameworkBundle/Test/TestContainer.php

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
use Symfony\Component\HttpKernel\KernelInterface;
1818

1919
/**
20+
* A special container used in tests. This gives access to both public and
21+
* private services. The container will not include private services that has
22+
* been inlined or removed. Private services will be removed when they are not
23+
* used by other services.
24+
*
2025
* @author Nicolas Grekas <[email protected]>
2126
*
2227
* @internal

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AutowiringTypesTest.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -25,36 +25,36 @@ public function testAnnotationReaderAutowiring()
2525
{
2626
static::bootKernel(['root_config' => 'no_annotations_cache.yml', 'environment' => 'no_annotations_cache']);
2727

28-
$annotationReader = static::$container->get('test.autowiring_types.autowired_services')->getAnnotationReader();
28+
$annotationReader = self::getContainer()->get('test.autowiring_types.autowired_services')->getAnnotationReader();
2929
$this->assertInstanceOf(AnnotationReader::class, $annotationReader);
3030
}
3131

3232
public function testCachedAnnotationReaderAutowiring()
3333
{
3434
static::bootKernel();
3535

36-
$annotationReader = static::$container->get('test.autowiring_types.autowired_services')->getAnnotationReader();
36+
$annotationReader = self::getContainer()->get('test.autowiring_types.autowired_services')->getAnnotationReader();
3737
$this->assertInstanceOf(class_exists(PsrCachedReader::class) ? PsrCachedReader::class : CachedReader::class, $annotationReader);
3838
}
3939

4040
public function testEventDispatcherAutowiring()
4141
{
4242
static::bootKernel(['debug' => false]);
4343

44-
$autowiredServices = static::$container->get('test.autowiring_types.autowired_services');
44+
$autowiredServices = self::getContainer()->get('test.autowiring_types.autowired_services');
4545
$this->assertInstanceOf(EventDispatcher::class, $autowiredServices->getDispatcher(), 'The event_dispatcher service should be injected if the debug is not enabled');
4646

4747
static::bootKernel(['debug' => true]);
4848

49-
$autowiredServices = static::$container->get('test.autowiring_types.autowired_services');
49+
$autowiredServices = self::getContainer()->get('test.autowiring_types.autowired_services');
5050
$this->assertInstanceOf(TraceableEventDispatcher::class, $autowiredServices->getDispatcher(), 'The debug.event_dispatcher service should be injected if the debug is enabled');
5151
}
5252

5353
public function testCacheAutowiring()
5454
{
5555
static::bootKernel();
5656

57-
$autowiredServices = static::$container->get('test.autowiring_types.autowired_services');
57+
$autowiredServices = self::getContainer()->get('test.autowiring_types.autowired_services');
5858
$this->assertInstanceOf(FilesystemAdapter::class, $autowiredServices->getCachePool());
5959
}
6060

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/BundlePathsTest.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ public function testBundlePublicDir()
4040
public function testBundleTwigTemplatesDir()
4141
{
4242
static::bootKernel(['test_case' => 'BundlePaths']);
43-
$twig = static::$container->get('twig.alias');
44-
$bundlesMetadata = static::$container->getParameter('kernel.bundles_metadata');
43+
$twig = static::getContainer()->get('twig.alias');
44+
$bundlesMetadata = static::getContainer()->getParameter('kernel.bundles_metadata');
4545

4646
$this->assertSame([$bundlesMetadata['LegacyBundle']['path'].'/Resources/views'], $twig->getLoader()->getPaths('Legacy'));
4747
$this->assertSame("OK\n", $twig->render('@Legacy/index.html.twig'));
@@ -53,7 +53,7 @@ public function testBundleTwigTemplatesDir()
5353
public function testBundleTranslationsDir()
5454
{
5555
static::bootKernel(['test_case' => 'BundlePaths']);
56-
$translator = static::$container->get('translator.alias');
56+
$translator = static::getContainer()->get('translator.alias');
5757

5858
$this->assertSame('OK', $translator->trans('ok_label', [], 'legacy'));
5959
$this->assertSame('OK', $translator->trans('ok_label', [], 'modern'));
@@ -62,7 +62,7 @@ public function testBundleTranslationsDir()
6262
public function testBundleValidationConfigDir()
6363
{
6464
static::bootKernel(['test_case' => 'BundlePaths']);
65-
$validator = static::$container->get('validator.alias');
65+
$validator = static::getContainer()->get('validator.alias');
6666

6767
$this->assertTrue($validator->hasMetadataFor(LegacyPerson::class));
6868
$this->assertCount(1, $constraintViolationList = $validator->validate(new LegacyPerson('john', 5)));
@@ -76,7 +76,7 @@ public function testBundleValidationConfigDir()
7676
public function testBundleSerializationConfigDir()
7777
{
7878
static::bootKernel(['test_case' => 'BundlePaths']);
79-
$serializer = static::$container->get('serializer.alias');
79+
$serializer = static::getContainer()->get('serializer.alias');
8080

8181
$this->assertEquals(['full_name' => 'john', 'age' => 5], $serializer->normalize(new LegacyPerson('john', 5), 'json'));
8282
$this->assertEquals(['full_name' => 'john', 'age' => 5], $serializer->normalize(new ModernPerson('john', 5), 'json'));

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolClearCommandTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public function testClearFailed()
8080
{
8181
$tester = $this->createCommandTester();
8282
/** @var FilesystemAdapter $pool */
83-
$pool = static::$container->get('cache.public_pool');
83+
$pool = static::getContainer()->get('cache.public_pool');
8484
$item = $pool->getItem('foo');
8585
$item->set('baz');
8686
$pool->save($item);
@@ -108,7 +108,7 @@ public function testClearFailed()
108108
private function createCommandTester()
109109
{
110110
$application = new Application(static::$kernel);
111-
$application->add(new CachePoolClearCommand(static::$container->get('cache.global_clearer')));
111+
$application->add(new CachePoolClearCommand(static::getContainer()->get('cache.global_clearer')));
112112

113113
return new CommandTester($application->find('cache:pool:clear'));
114114
}

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public function testRedisCustomCachePools()
7878
private function doTestCachePools($options, $adapterClass)
7979
{
8080
static::bootKernel($options);
81-
$container = static::$container;
81+
$container = static::getContainer();
8282

8383
$pool1 = $container->get('cache.pool1');
8484
$this->assertInstanceOf($adapterClass, $pool1);

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php

+7-7
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ public function testDumpContainerIfNotExists()
2727
$application = new Application(static::$kernel);
2828
$application->setAutoExit(false);
2929

30-
@unlink(static::$container->getParameter('debug.container.dump'));
30+
@unlink(static::getContainer()->getParameter('debug.container.dump'));
3131

3232
$tester = new ApplicationTester($application);
3333
$tester->run(['command' => 'debug:container']);
3434

35-
$this->assertFileExists(static::$container->getParameter('debug.container.dump'));
35+
$this->assertFileExists(static::getContainer()->getParameter('debug.container.dump'));
3636
}
3737

3838
public function testNoDebug()
@@ -91,7 +91,7 @@ public function testDescribeEnvVars()
9191
$application = new Application(static::$kernel);
9292
$application->setAutoExit(false);
9393

94-
@unlink(static::$container->getParameter('debug.container.dump'));
94+
@unlink(static::getContainer()->getParameter('debug.container.dump'));
9595

9696
$tester = new ApplicationTester($application);
9797
$tester->run(['command' => 'debug:container', '--env-vars' => true], ['decorated' => false]);
@@ -128,7 +128,7 @@ public function testDescribeEnvVar()
128128
$application = new Application(static::$kernel);
129129
$application->setAutoExit(false);
130130

131-
@unlink(static::$container->getParameter('debug.container.dump'));
131+
@unlink(static::getContainer()->getParameter('debug.container.dump'));
132132

133133
$tester = new ApplicationTester($application);
134134
$tester->run(['command' => 'debug:container', '--env-var' => 'js'], ['decorated' => false]);
@@ -156,7 +156,7 @@ public function testGetDeprecation()
156156
$application = new Application(static::$kernel);
157157
$application->setAutoExit(false);
158158

159-
@unlink(static::$container->getParameter('debug.container.dump'));
159+
@unlink(static::getContainer()->getParameter('debug.container.dump'));
160160

161161
$tester = new ApplicationTester($application);
162162
$tester->run(['command' => 'debug:container', '--deprecations' => true]);
@@ -176,7 +176,7 @@ public function testGetDeprecationNone()
176176
$application = new Application(static::$kernel);
177177
$application->setAutoExit(false);
178178

179-
@unlink(static::$container->getParameter('debug.container.dump'));
179+
@unlink(static::getContainer()->getParameter('debug.container.dump'));
180180

181181
$tester = new ApplicationTester($application);
182182
$tester->run(['command' => 'debug:container', '--deprecations' => true]);
@@ -194,7 +194,7 @@ public function testGetDeprecationNoFile()
194194
$application = new Application(static::$kernel);
195195
$application->setAutoExit(false);
196196

197-
@unlink(static::$container->getParameter('debug.container.dump'));
197+
@unlink(static::getContainer()->getParameter('debug.container.dump'));
198198

199199
$tester = new ApplicationTester($application);
200200
$tester->run(['command' => 'debug:container', '--deprecations' => true]);

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDumpTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ public function testContainerCompilationInDebug()
2020
{
2121
$this->createClient(['test_case' => 'ContainerDump', 'root_config' => 'config.yml']);
2222

23-
$this->assertTrue(static::$container->has('serializer'));
23+
$this->assertTrue(static::getContainer()->has('serializer'));
2424
}
2525

2626
public function testContainerCompilation()
2727
{
2828
$this->createClient(['test_case' => 'ContainerDump', 'root_config' => 'config.yml', 'debug' => false]);
2929

30-
$this->assertTrue(static::$container->has('serializer'));
30+
$this->assertTrue(static::getContainer()->has('serializer'));
3131
}
3232
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;
13+
14+
use Symfony\Bundle\FrameworkBundle\Test\TestContainer;
15+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestServiceContainer\NonPublicService;
16+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestServiceContainer\PrivateService;
17+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestServiceContainer\PublicService;
18+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestServiceContainer\UnusedPrivateService;
19+
use Symfony\Component\DependencyInjection\ContainerInterface;
20+
21+
class KernelTestCaseTest extends AbstractWebTestCase
22+
{
23+
public function testThatPrivateServicesAreUnavailableIfTestConfigIsDisabled()
24+
{
25+
static::bootKernel(['test_case' => 'TestServiceContainer', 'root_config' => 'test_disabled.yml', 'environment' => 'test_disabled']);
26+
27+
$this->expectException(\LogicException::class);
28+
static::getContainer();
29+
}
30+
31+
public function testThatPrivateServicesAreAvailableIfTestConfigIsEnabled()
32+
{
33+
static::bootKernel(['test_case' => 'TestServiceContainer']);
34+
35+
$container = static::getContainer();
36+
$this->assertInstanceOf(ContainerInterface::class, $container);
37+
$this->assertInstanceOf(TestContainer::class, $container);
38+
$this->assertTrue($container->has(PublicService::class));
39+
$this->assertTrue($container->has(NonPublicService::class));
40+
$this->assertTrue($container->has(PrivateService::class));
41+
$this->assertTrue($container->has('private_service'));
42+
$this->assertFalse($container->has(UnusedPrivateService::class));
43+
}
44+
}

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/MailerTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public function testEnvelopeListener()
2828
$this->assertEquals('[email protected]', $envelope->getSender()->getAddress());
2929
};
3030

31-
$eventDispatcher = self::$container->get(EventDispatcherInterface::class);
32-
$logger = self::$container->get('logger');
31+
$eventDispatcher = self::getContainer()->get(EventDispatcherInterface::class);
32+
$logger = self::getContainer()->get('logger');
3333

3434
$testTransport = new class($eventDispatcher, $logger, $onDoSend) extends AbstractTransport {
3535
/**

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/PropertyInfoTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public function testPhpDocPriority()
1919
{
2020
static::bootKernel(['test_case' => 'Serializer']);
2121

22-
$this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT))], static::$container->get('property_info')->getTypes('Symfony\Bundle\FrameworkBundle\Tests\Functional\Dummy', 'codes'));
22+
$this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT))], static::getContainer()->get('property_info')->getTypes('Symfony\Bundle\FrameworkBundle\Tests\Functional\Dummy', 'codes'));
2323
}
2424
}
2525

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public function testDeserializeArrayOfObject()
2020
{
2121
static::bootKernel(['test_case' => 'Serializer']);
2222

23-
$result = static::$container->get('serializer.alias')->deserialize('{"bars": [{"id": 1}, {"id": 2}]}', Foo::class, 'json');
23+
$result = static::getContainer()->get('serializer.alias')->deserialize('{"bars": [{"id": 1}, {"id": 2}]}', Foo::class, 'json');
2424

2525
$bar1 = new Bar();
2626
$bar1->id = 1;

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/TestServiceContainerTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020

2121
class TestServiceContainerTest extends AbstractWebTestCase
2222
{
23+
/**
24+
* @group legacy
25+
*/
2326
public function testThatPrivateServicesAreUnavailableIfTestConfigIsDisabled()
2427
{
2528
static::bootKernel(['test_case' => 'TestServiceContainer', 'root_config' => 'test_disabled.yml', 'environment' => 'test_disabled']);
@@ -33,6 +36,9 @@ public function testThatPrivateServicesAreUnavailableIfTestConfigIsDisabled()
3336
$this->assertFalse(static::$container->has(UnusedPrivateService::class));
3437
}
3538

39+
/**
40+
* @group legacy
41+
*/
3642
public function testThatPrivateServicesAreAvailableIfTestConfigIsEnabled()
3743
{
3844
static::bootKernel(['test_case' => 'TestServiceContainer']);

src/Symfony/Bundle/SecurityBundle/Tests/Functional/AutowiringTypesTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ public function testAccessDecisionManagerAutowiring()
2121
{
2222
static::bootKernel(['debug' => false]);
2323

24-
$autowiredServices = static::$container->get('test.autowiring_types.autowired_services');
24+
$autowiredServices = static::getContainer()->get('test.autowiring_types.autowired_services');
2525
$this->assertInstanceOf(AccessDecisionManager::class, $autowiredServices->getAccessDecisionManager(), 'The security.access.decision_manager service should be injected in debug mode');
2626

2727
static::bootKernel(['debug' => true]);
2828

29-
$autowiredServices = static::$container->get('test.autowiring_types.autowired_services');
29+
$autowiredServices = static::getContainer()->get('test.autowiring_types.autowired_services');
3030
$this->assertInstanceOf(TraceableAccessDecisionManager::class, $autowiredServices->getAccessDecisionManager(), 'The debug.security.access.decision_manager service should be injected in non-debug mode');
3131
}
3232

0 commit comments

Comments
 (0)