在Symfony 5中运行WebTestCase之前,如何加载fixture?
在不安装任何附加捆绑包的情况下,您使用哪些方法?
class MyPageControllerTest extends WebTestCase
{
public function testIndexAction()
{
$client = static::createClient();
$crawler = $client->request('GET', '/');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$table = $crawler->filter('.table-emp');
$this->assertCount(1, $table->filter('tbody tr'));
}
}
WebTestCase
扩展了Symfony的KernelTestCase
,后者扩展了PHPUnit的TestCase。PhpUnit几乎没有什么重要的方法。
// executed once before the first test
public static void setUpBeforeClass();
// executed once before the last test
public static void tearDownAfterClass();
// executed once for every test before it starts
public void setUp();
// executed once for every test after it ends
public void tearDown();
如果您想为每个测试都有一个新的测试数据库,我会创建一个bash脚本来执行MySQL文件并将数据放在适当的位置。这样做可以让你对正在发生的事情和地点有很大的控制权。
我认为,为了创建可重用和一致的东西,您可以创建自己的测试用例,以扩展现有的Symfony Web测试用例。
通过这种方式,您将获得一个额外的抽象层,在该抽象层中,您可以设置所有的fixture设置和所有其他可能在web(或API,如果您对API测试也这样做的话(测试中需要的额外功能。
另一个不需要使用额外类的解决方案可能涉及特性并将函数放在那里。
我个人建议自定义父类,它具有一些特性,可以在不同类型的测试(如(之间共享功能
class MyAwesomeParentTestClass
{
use AwesomeFeatureTrait;
use CustomAssertionsTrait;
}
我知道你不想使用额外的捆绑包,所以这只是一个额外的建议。如果您决定使用捆绑包,我建议您使用以下内容:Alice。
就我个人而言,每次运行phpunit时,我都有一个删除测试数据库并插入fixture的命令:
在tests/bootstrap.php
:中
<?php
declare(strict_types=1);
use AppCommandSetupCommand;
use AppKernel;
use SymfonyBundleFrameworkBundleConsoleApplication;
use SymfonyComponentConsoleInputArrayInput;
use SymfonyComponentConsoleOutputConsoleOutput;
use SymfonyComponentDotenvDotenv;
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);
如果你想要我的自定义命令来做所有这些事情:
<?php
declare(strict_types=1);
namespace AppCommand;
use SymfonyBundleFrameworkBundleConsoleApplication;
use SymfonyComponentConsoleAttributeAsCommand;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputArrayInput;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputNullOutput;
use SymfonyComponentConsoleOutputOutputInterface;
use SymfonyComponentConsoleStyleSymfonyStyle;
use SymfonyComponentHttpKernelKernelInterface;
/**
* @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;
}
}
然后,我使用dmaicher/doctrine测试捆绑包使测试相互隔离(这个捆绑包基本上每个都将测试封装在事务中,并在每个测试之间回滚(。这样,您就不必在每次测试之间加载固定装置。