drjele/symfony-phpunit
Symfony bridge for running PHPUnit tests with a simple, consistent setup. Provides helpers and configuration to integrate PHPUnit into Symfony projects, smoothing bootstrapping and test execution across environments.
Installation Add the package to your Laravel project via Composer:
composer require drjele/symfony-phpunit --dev
Ensure phpunit is listed in require-dev in composer.json.
Basic Configuration The package extends Symfony’s PHPUnit bridge, so no Laravel-specific config is needed. Start by publishing the default PHPUnit config (if using Symfony’s bridge):
php vendor/bin/phpunit --configuration=phpunit.xml.dist
For Laravel, ensure your phpunit.xml extends Symfony’s bridge (if applicable) or use Laravel’s default config.
First Use Case: Data Providers
Simplify test data setup with Symfony’s DataProvider trait:
use Symfony\Component\DependencyInjection\Tests\DataProvider\DataProviderInterface;
class UserTest extends TestCase implements DataProviderInterface
{
public function provideTestData()
{
return [
'valid user' => [['name' => 'John', 'email' => '[email protected]']],
'invalid email' => [['name' => 'Jane', 'email' => 'invalid-email']],
];
}
/**
* @dataProvider provideTestData
*/
public function testUserCreation(array $userData)
{
$user = User::create($userData);
$this->assertDatabaseHas('users', $userData);
}
}
Leverage Symfony’s DI container for test dependencies:
use Symfony\Component\DependencyInjection\ContainerInterface;
class ServiceTest extends TestCase
{
private ContainerInterface $container;
protected function setUp(): void
{
$this->container = self::getContainer();
}
public function testServiceDependency()
{
$service = $this->container->get('App\\Services\\UserService');
$this->assertInstanceOf(UserService::class, $service);
}
}
Tip: Use Laravel’s createApplication() in TestCase to access the container.
Replace manual mocks with Symfony’s ExpressionLanguage for dynamic test logic:
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
class OrderTest extends TestCase
{
public function testOrderStatus()
{
$language = new ExpressionLanguage();
$expression = $language->parse('order.status === "paid"');
$result = $expression->evaluate([
'order' => ['status' => 'paid', 'amount' => 100],
]);
$this->assertTrue($result);
}
}
Combine with Laravel’s RefreshDatabase or MigrateFreshDatabase traits:
use Illuminate\Foundation\Testing\RefreshDatabase;
use Symfony\Component\DependencyInjection\Tests\DataProvider\DataProviderInterface;
class MigrationTest extends TestCase implements DataProviderInterface
{
use RefreshDatabase;
public function provideMigrations()
{
return [
'users table' => ['users'],
'posts table' => ['posts'],
];
}
/**
* @dataProvider provideMigrations
*/
public function testTableExists(string $table)
{
$this->assertTrue(Schema::hasTable($table));
}
}
Extend Symfony’s assertions for domain-specific checks:
use Symfony\Component\Validator\Tests\Constraint\Valid;
class ValidatorTest extends TestCase
{
public function testEmailValidation()
{
$validator = $this->container->get('validator');
$constraint = new Valid();
$violations = $validator->validate('invalid-email', $constraint);
$this->assertCount(1, $violations);
}
}
Container Initialization
setUp():
protected function setUp(): void
{
$this->app = require __DIR__.'/../../bootstrap/app.php';
$this->app->make(Kernel::class)->boot();
}
DataProvider Conflicts
DataProvider trait.use Symfony\Component\DependencyInjection\Tests\DataProvider\DataProviderInterface;
ExpressionLanguage Security
ExpressionLanguage constructor:
$language = new ExpressionLanguage([
'functions' => ['strtolower' => 'strtolower'],
'variables' => ['order' => $orderData],
]);
Enable Symfony Debug Mode
Add to phpunit.xml:
<php>
<env name="SYMFONY_DEBUG" value="1"/>
</php>
Dump Container Services Inspect available services:
$services = $this->container->getServiceIds();
dump($services);
Test Isolation
Use KernelInterface::SHARE_EMPTY_ENVIRONMENT to avoid shared state:
$kernel = self::bootKernel(['environment' => 'test']);
Custom Data Providers Create reusable providers for complex test data:
class UserDataProvider implements DataProviderInterface
{
public function provideUsers()
{
return [
'admin' => [['role' => 'admin']],
'guest' => [['role' => 'guest']],
];
}
}
Hook into Symfony Events Extend test lifecycle with Symfony’s event system:
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class TestEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
'kernel.test' => 'onTestStart',
];
}
public function onTestStart()
{
// Pre-test logic (e.g., seed DB)
}
}
Laravel-Specific Extensions
Override Symfony’s TestCase to integrate with Laravel’s helpers:
abstract class LaravelSymfonyTestCase extends TestCase
{
use CreatesApplication;
protected function getContainer()
{
return $this->app->get('kernel')->getContainer();
}
}
How can I help you explore Laravel packages today?