Last active
August 31, 2023 07:32
-
-
Save lyrixx/0adb8fd414451596557871d2d9af5695 to your computer and use it in GitHub Desktop.
Test applications services can boot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Tests\Integration; | |
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; | |
use Symfony\Component\Config\FileLocator; | |
use Symfony\Component\DependencyInjection\ContainerBuilder; | |
use Symfony\Component\DependencyInjection\Definition; | |
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; | |
class ContainerTest extends KernelTestCase | |
{ | |
private const FILTER_LIST = [ | |
// some services can exist only in dev or prod (thus not in test env) | |
// or some services are behind some features flags | |
// or some services are static (thus they are not real service) | |
]; | |
public function testContainer() | |
{ | |
static::bootKernel(['debug' => true]); | |
$projectDir = static::getContainer()->getParameter('kernel.project_dir'); | |
$container = static::getContainer(); | |
$builder = new ContainerBuilder(); | |
$loader = new XmlFileLoader($builder, new FileLocator()); | |
$loader->load($container->getParameter('debug.container.dump')); | |
$count = 0; | |
foreach ($builder->getDefinitions() as $id => $service) { | |
if ($this->isSkipped($id, $service, $builder, $projectDir)) { | |
continue; | |
} | |
$container->get($id); | |
++$count; | |
} | |
$this->addToAssertionCount($count); | |
} | |
private function isSkipped(string $id, Definition $service, ContainerBuilder $builder, string $projectDir): bool | |
{ | |
if (str_starts_with($id, '.instanceof.') || str_starts_with($id, '.abstract.') || str_starts_with($id, '.errored.')) { | |
return true; // Symfony internal stuff | |
} | |
if ($service->isAbstract()) { | |
return true; // Symfony internal stuff | |
} | |
$class = $service->getClass(); | |
if (!$class) { | |
return true; // kernel, or alias, or abstract | |
} | |
if (\in_array($class, self::FILTER_LIST)) { | |
return true; | |
} | |
$rc = $builder->getReflectionClass($class, false); | |
if (!$rc) { | |
return true; | |
} | |
$filename = $rc->getFileName(); | |
if (!str_starts_with($filename, "{$projectDir}/src")) { | |
return true; // service class not in tests/Integration | |
} | |
if ($rc->isAbstract()) { | |
return true; | |
} | |
return false; | |
} | |
Thanks for sharing this.
You can use this to automatically find the XML file:
$xml = file_get_contents($container->getParameter('debug.container.dump'));
Another improvement would be to use the XmlFileLoader so that you don't have to manually parse and work with the XML structure:
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator());
$loader->load(static::getContainer()->getParameter('debug.container.dump'));
foreach ($container->getDefinitions() as $service) {
// $service = Definition now...
}
Very good idea. I'll update the gist asap
Gist updated 🎉 Thanks again
In our project, we have a lot of service locators. I think the test should take these into account and unpack every ServiceLocator that it finds.
Another optimization that can be made: report all failures, instead of only the first one.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@javaDeveloperKid you're right. I coded that only for my use case. But feel free to adapt it!