Skip to content

Instantly share code, notes, and snippets.

@lyrixx
Last active August 31, 2023 07:32
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lyrixx/0adb8fd414451596557871d2d9af5695 to your computer and use it in GitHub Desktop.
Save lyrixx/0adb8fd414451596557871d2d9af5695 to your computer and use it in GitHub Desktop.
Test applications services can boot
<?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;
}
@lyrixx
Copy link
Author

lyrixx commented Sep 28, 2022

@javaDeveloperKid you're right. I coded that only for my use case. But feel free to adapt it!

@ruudk
Copy link

ruudk commented Aug 7, 2023

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...
}

@lyrixx
Copy link
Author

lyrixx commented Aug 7, 2023

Very good idea. I'll update the gist asap

@lyrixx
Copy link
Author

lyrixx commented Aug 24, 2023

Gist updated 🎉 Thanks again

@ruudk
Copy link

ruudk commented Aug 31, 2023

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.

@ruudk
Copy link

ruudk commented Aug 31, 2023

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