php artisan boost:add-skill symkit/settings-bundle
Save this content to: .cursor/rules/bundle-tests.mdc
# Bundle Test Conventions
## General
- `declare(strict_types=1)` in test files too
- Structure: `tests/Unit/`, `tests/Integration/`, `tests/Functional/` when applicable; for small bundles a lighter layout is fine (e.g. only `tests/Integration/`)
- PHPUnit testsuite name in `phpunit.xml.dist` can stay generic (e.g. "Unit") even if test classes live under `Integration/`
- Use `nyholm/symfony-bundle-test` with `TestKernel` for integration tests
## Minimum Required Tests
- Bundle boots without error
- All services are correctly registered and wired
- Configuration validation (required keys, default values, custom values)
- Twig prepend (paths registered, globals set) when applicable
## Exception Handler Cleanup
Symfony's TestKernel registers exception handlers. Always restore them in `tearDown()` to avoid PHPUnit "risky test" warnings:
```php
protected function tearDown(): void
{
parent::tearDown();
\restore_exception_handler();
}
```
## Integration Test Template
```php
use Nyholm\BundleTest\TestKernel;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpKernel\KernelInterface;
final class BundleBootTest extends KernelTestCase
{
protected function tearDown(): void
{
parent::tearDown();
\restore_exception_handler();
}
protected static function getKernelClass(): string
{
return TestKernel::class;
}
protected static function createKernel(array $options = []): KernelInterface
{
/** @var TestKernel $kernel */
$kernel = parent::createKernel($options);
$kernel->addTestBundle(VendorNameBundle::class);
$kernel->addTestConfig(static function ($container): void {
$container->loadFromExtension('framework', ['test' => true]);
});
$kernel->handleOptions($options);
return $kernel;
}
}
```
Key patterns:
- `/** @var TestKernel $kernel */` for PHPStan compatibility
- `static function` closures in `addTestConfig()` (no `$this` binding)
- Register TwigBundle when testing Twig integration (globals, paths)
- `$kernel->getContainer()->get('test.service_container')->get('twig')` for Twig access in custom-booted kernels
## PHPUnit Configuration
- `failOnRisky="true"` in `phpunit.xml.dist` (enforce clean tests)
- Always pair with `restore_exception_handler()` in `tearDown()` to pass risky check
## Live Components
Test with `InteractsWithLiveComponents` trait in `KernelTestCase`:
```php
use Symfony\UX\LiveComponent\Test\InteractsWithLiveComponents;
$component = $this->createLiveComponent(name: SearchLive::class, data: ['query' => 'test']);
$component->call('loadMore');
self::assertSame(2, $component->component()->page);
```
## Twig Components
Test rendering via Twig render assertions or by checking rendered HTML output.
## After Every Change
Run `make test` then `make infection` to verify coverage and mutation score.
declare(strict_types=1) in test files tootests/Unit/, tests/Integration/, tests/Functional/ when applicable; for small bundles a lighter layout is fine (e.g. only tests/Integration/)phpunit.xml.dist can stay generic (e.g. "Unit") even if test classes live under Integration/nyholm/symfony-bundle-test with TestKernel for integration testsSymfony's TestKernel registers exception handlers. Always restore them in tearDown() to avoid PHPUnit "risky test" warnings:
protected function tearDown(): void
{
parent::tearDown();
\restore_exception_handler();
}
use Nyholm\BundleTest\TestKernel;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpKernel\KernelInterface;
final class BundleBootTest extends KernelTestCase
{
protected function tearDown(): void
{
parent::tearDown();
\restore_exception_handler();
}
protected static function getKernelClass(): string
{
return TestKernel::class;
}
protected static function createKernel(array $options = []): KernelInterface
{
/** @var TestKernel $kernel */
$kernel = parent::createKernel($options);
$kernel->addTestBundle(VendorNameBundle::class);
$kernel->addTestConfig(static function ($container): void {
$container->loadFromExtension('framework', ['test' => true]);
});
$kernel->handleOptions($options);
return $kernel;
}
}
Key patterns:
/** @var TestKernel $kernel */ for PHPStan compatibilitystatic function closures in addTestConfig() (no $this binding)$kernel->getContainer()->get('test.service_container')->get('twig') for Twig access in custom-booted kernelsfailOnRisky="true" in phpunit.xml.dist (enforce clean tests)restore_exception_handler() in tearDown() to pass risky checkTest with InteractsWithLiveComponents trait in KernelTestCase:
use Symfony\UX\LiveComponent\Test\InteractsWithLiveComponents;
$component = $this->createLiveComponent(name: SearchLive::class, data: ['query' => 'test']);
$component->call('loadMore');
self::assertSame(2, $component->component()->page);
Test rendering via Twig render assertions or by checking rendered HTML output.
Run make test then make infection to verify coverage and mutation score.
How can I help you explore Laravel packages today?