Моя ситуация
У меня есть проект Symfony 4.2 со следующей структурой:
- src
- Контроллер
- Сервис
- Twig Extensions
- Шаблоны
- Тесты
Я использую класс базы данных, который внутренне создает соединение PDO.Если я запускаю свои тесты с PHPUnit, мой класс базы данных должен переключиться с mysql на sqlite.Здесь все отлично работает.
Моя проблема
Я не могу сохранить один раз созданный экземпляр базы данных, когда выполняется только один тест.Кажется, Symfony воссоздает его во время теста: когда он находится внутри шаблона Twig, который использует расширение Twig.Поскольку класс базы данных использует
new \PDO('sqlite::memory:');
, он теряет созданные таблицы и, следовательно, тест не пройден.Мне известно, что экземпляр базы данных (со ссылкой на PDO) сбрасывается после каждого теста, но в моей ситуации у меня только один тест. Как я могу убедиться, что он повторно использует экземпляр базы данных?
Здесь соответствующий код
Класс InstanceExtension предоставляет заголовок функции, который используется в шаблоне Twig.и должен получить доступ к базе данных.
<?php
namespace App\TwigExtension;
use App\Service\Database;
use Twig\TwigFunction;
use Twig\Extension\AbstractExtension;
class InstanceExtension extends AbstractExtension
{
protected $db;
/**
* @param Database $db
*/
public function __construct(Database $db)
{
$this->db = $db;
}
/**
* Tries to return the title/name for a given ID.
*
* @param string $subject
* @param string|array $tables
* @param string $lang Default is 'de'
*
* @return string label or id, if no title/name was found
*/
public function title(string $subject, $tables, string $lang = 'de'): string
{
return $this->db->get_instance_title($subject, $tables, $lang);
}
}
В моем services.yaml класс Database
установлен в public (что должно позволить его повторное использование, не так ли?):
App\Service\Database:
public: true
Класс базы данных
Здесь часть класса Database, которая инициализирует соединение PDO.Удален производственный код, использующий MySQL:
<?php
declare(strict_types=1);
namespace App\Service;
class Database
{
/**
* @param Config $app_config
*
* @throws \Exception
*/
public function __construct(Config $app_config)
{
global $sqlite_pdo;
try {
// non test
// ..
// test environment
} else {
$this->db_engine = 'sqlite';
// workaround to ensure we have the same PDO instance everytime we use the Database instance
// if we dont use it, in Twig extensions a new Database instance is created with a new SQLite
// database, which is empty.
if (null == $sqlite_pdo) {
$pdo = new \PDO('sqlite::memory:');
$sqlite_pdo = $pdo;
} else {
$pdo = $sqlite_pdo;
}
}
} catch (\PDOException $e) {
if (\strpos((string) $e->getMessage(), 'could not find driver') !== false) {
throw new \Exception(
'Could not create a PDO connection. Is the driver installed/enabled?'
);
}
if (\strpos((string) $e->getMessage(), 'unknown database') !== false) {
throw new \Exception(
'Could not create a PDO connection. Check that your database exists.'
);
}
// Don't leak credentials directly if we can.
throw new \Exception(
'Could not create a PDO connection. Please check your username and password.'
);
}
if ('mysql' == $this->db_engine) {
// ...
}
// default fetch mode is associative
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
// everything odd will be handled as exception
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$this->db = $pdo;
}
// ...
}
Один тест выглядит примерно так:
<?php
class SalesChannelControllerTest extends TestCase
{
public function setUp()
{
parent::setUp();
// init Database class, using SQLite
$this->init_db();
// further setup function calls
// SalesChannelController is a child of
// Symfony\Bundle\FrameworkBundle\Controller\AbstractController
$this->fixture = new SalesChannelController(
$this->db
// ...
);
}
/**
* Returns a ready-to-use instance of the database. Default adapter is SQLite.
*
* @return Database
*/
protected function init_db(): Database
{
// config parameter just contains DB credentials
$this->db = new Database($this->config);
}
public function test_introduction_action()
{
// preparations
/*
* run action
*
* which renders a Twig template, creates a Response and returns it.
*/
$response = $this->fixture->introduction_action($this->request, $this->session, 'sales_channel1');
/*
* check response
*/
$this->assertEquals(200, $response->getStatusCode());
}
}
Мой текущий обходной путь - сохранить экземпляр PDO в глобальной переменной.и повторно используйте его, если требуется.
<?php
global $sqlite_pdo;
// ...
// inside Database class, when initializing the PDO connection
if (null == $sqlite_pdo) {
$pdo = new \PDO('sqlite::memory:');
$sqlite_pdo = $pdo;
} else {
$pdo = $sqlite_pdo;
}
Если вам нужна дополнительная информация, пожалуйста, сообщите мне.Спасибо за ваше время и помощь заранее!