facile-it/symfony-functional-testcase
A base functional test case for Symfony applications that simplifies bootstrapping the kernel and writing integration tests for your services, controllers, and HTTP layer.
This is a Symfony testing package — it is not a Laravel package, so the only reason a Laravel codebase pulls it in is that you depend on a Symfony bundle/component (or maintain one) and need a booted kernel under test. Confirm that before adding a dependency that has nothing to do with Illuminate\Foundation\Testing.
composer require --dev facile-it/symfony-functional-testcase
Hard requirements as of 1.5.0:
Where to look first:
WebTestCase — the class you extend; it layers convenience helpers over Symfony's own KernelTestCase/WebTestCase.getContainer() — the native accessor you should now be using.getDependencyInjectionContainer() — deprecated in 1.5.0, still functional, slated for removal.First use case — boot the kernel, grab a service, assert on it:
use Facile\SymfonyFunctionalTestCase\WebTestCase;
final class BillingServiceTest extends WebTestCase
{
public function testInvoiceIsGenerated(): void
{
self::createClient();
$billing = self::getContainer()->get(BillingService::class);
self::assertTrue($billing->invoice()->isPaid());
}
}
If you are inside a Laravel app, register these tests in a separate PHPUnit testsuite with their own bootstrap and their own KERNEL_CLASS; do not mix them into the testsuite that boots Laravel's application container.
Service access via the test container. The container returned by getContainer() is the test container: private services are exposed, which is precisely the behaviour you want in tests and precisely why you should not use this accessor in production code paths.
// 1.5.0+
$mailer = self::getContainer()->get(Symfony\Component\Mailer\MailerInterface::class);
// Pre-1.5 style, triggers a deprecation path
$mailer = self::getDependencyInjectionContainer()->get(MailerInterface::class);
Mocking by container substitution. The common workflow is to boot the client, then replace a service before exercising an HTTP request:
self::createClient();
$container = self::getContainer();
$container->set(ClockInterface::class, new FrozenClock(new \DateTimeImmutable('2024-01-01')));
$client = self::getClient();
$client->request('GET', '/orders');
Note the ordering constraint: set services after createClient()/bootKernel() but before the kernel handles a request that would instantiate them.
Configuration overrides per test. Override container parameters or bundle config before boot:
protected static function getKernelClass(): string
{
return TestKernel::class;
}
Use a dedicated TestKernel that registers only the bundles your test needs plus your config fixture — this is the standard pattern and the main extension point of the package.
Data-driven and attribute-based tests. Pair with PHPUnit attributes and #[DataProvider]; boot once per test method and keep assertions on services, not on the container itself.
Hybrid Laravel/Symfony repositories. Keep two bootstrap files (tests/bootstrap-laravel.php, tests/bootstrap-symfony.php) and two <testsuite> entries. Anything else and you will be debugging why app() returns a Symfony container.
^1.4 and treat this upgrade as a separate, deliberate task. Bumping the constraint silently will fail on the lowest job.getDependencyInjectionContainer() still works in 1.5.0. Migrate to getContainer() now — a find-and-replace across the test suite is cheap today and mandatory in 2.0. Watch deprecation output in CI logs; it is easy to miss because functional tests suppress notices by default.getContainer() returns Symfony's test container, which exposes private services and lets you set() instances. A test passing because of a set() does not prove the production wiring works — assert at least once per service that the real, unmodified container can instantiate it.setUp() or inside the test method; stale references point at a shut-down kernel and produce confusing "service not found" or serialization errors..env.test and seeing no effect almost always means the kernel cache directory. Clear it (KERNEL_CLASS / APP_ENV=test cache path) rather than debugging your YAML.bin/console debug:container --env=test and bin/console debug:config <bundle> --env=test tell you exactly what the booted kernel sees, which is frequently different from config/packages/*.yaml because of test-only overrides.getKernelClass(), createKernel(), and a custom TestKernel for bundle registration. Prefer these over global PHPUnit bootstrap hacks; the package's whole value is that per-test kernel configuration stays declarative.How can I help you explore Laravel packages today?