Personally, I have a command that deletes my test database and insert my fixtures every time I run phpunit :
in tests/bootstrap.php
:
<?php
declare(strict_types=1);
use App\Command\SetupCommand;
use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Dotenv\Dotenv;
require dirname(__DIR__).'/vendor/autoload.php';
if (file_exists(dirname(__DIR__).'/config/bootstrap.php')) {
require dirname(__DIR__).'/config/bootstrap.php';
} elseif (method_exists(Dotenv::class, 'bootEnv')) {
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
}
// https://symfony.com/doc/current/testing.html#set-up-your-test-environment
$kernel = new Kernel(environment: 'test', debug: false);
$kernel->boot();
$application = new Application($kernel);
// Run the app:setup command before running phpunit
$command = new SetupCommand($kernel);
$command->run(new ArrayInput([]), new ConsoleOutput);
If you want my custom command that does all these things:
<?php
declare(strict_types=1);
namespace App\Command;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* @codeCoverageIgnore
*/
#[AsCommand(
name: 'app:setup',
description: 'Setup the app for dev environment',
)]
class SetupCommand extends Command
{
/**
* @override
*
* {@inheritDoc}
*/
public function __construct(private readonly KernelInterface $kernel)
{
parent::__construct();
}
/**
* @override
*
* {@inheritDoc}
*/
protected function execute(
InputInterface $input,
OutputInterface $output
): int {
$io = new SymfonyStyle($input, $output);
$io->title('Installation assistant');
$io->warning('This command should not be executed in production !');
$io->warning('Database suppression...');
$application = $this->getApplication();
if (!$application) {
$application = new Application($this->kernel);
$this->setApplication($application);
}
$returnCode = $application
->get('doctrine:database:drop')
->run(new ArrayInput([
'--if-exists' => true,
'--force' => true
]), new NullOutput)
;
if (0 === $returnCode) {
$io->success('The database has been deleted');
$io->warning('Database creation...');
$returnCode = $application
->get('doctrine:database:create')
->run(new ArrayInput([]), new NullOutput)
;
}
if (0 === $returnCode) {
$io->success('The database has been created');
$io->warning('Database schema creation...');
$returnCode = $application
->get('doctrine:schema:create')
->run(new ArrayInput([]), new NullOutput)
;
}
if (0 === $returnCode) {
$io->success('The database schema has been created');
$io->warning('Executing migrations...');
$args = new ArrayInput([
'--no-interaction' => true
]);
$args->setInteractive(false);
$returnCode = $application
->get('doctrine:migrations:migrate')
->run($args, new NullOutput)
;
}
if (0 === $returnCode) {
$io->success('The migrations have been executed');
$io->warning('Adding test/dummy data...');
$args = new ArrayInput([
'--no-interaction' => true,
'--purge-with-truncate' => true
]);
$args->setInteractive(false);
$returnCode = $application
->get('doctrine:fixtures:load')
->run($args, new NullOutput);
}
if (0 === $returnCode) {
$io->success('The data has been created');
$io->warning('Clearing Symfony cache...');
$returnCode = $application
->get('cache:clear')
->run(new ArrayInput([]), new NullOutput);
}
if (0 === $returnCode) {
$io->success('The Symfony cache has been cleared');
$io->warning('Clearing Doctrine Query cache...');
$returnCode = $application
->get('doctrine:cache:clear-query')
->run(new ArrayInput([]), new NullOutput);
}
if (0 === $returnCode) {
$io->success('The Doctrine Query cache has been cleared');
$io->warning('Clearing Doctrine Result cache...');
$returnCode = $application
->get('doctrine:cache:clear-result')
->run(new ArrayInput([]), new NullOutput);
}
if (0 === $returnCode) {
$io->success('The Doctrine Result cache has been cleared');
if (function_exists('apcu_clear_cache')) {
$io->warning('Clearing APC cache...');
apcu_clear_cache();
$io->success('APCu cache has been cleared');
}
}
return 0;
}
}
Then I used dmaicher/doctrine-test-bundle to have tests isolated from each other (this bundle basically each wrap tests in a transaction and rollback between each tests). This way, you don't have to load your fixtures between each of your tests.