Nette\Tester a opakovaná inicializace databáze
- radas
- Člen | 224
Ahoj,
chci testovat třídy představující modelovou část aplikace. V určitých
chvílích by se mi hodilo mít databázi inicializovanou do určitého stavu.
Mám proto SQL skript, který se postará o dropnutí a vytvoření tabulek a
jejich naplnění nějakými daty. Tento skript pak u některých testů
spouštím v konstruktoru takto:
class SettingTest extends TestCase
{
private $container;
private $setting;
public function __construct(Container $container)
{
$this->container = $container;
$this->setting = new Setting($container);
$this->container->getService('database')->loadFile(__DIR__.'/../initialization.sql');
}
public function testMethod1...() {}
...
Problém je ten, pokud se inicializace spouští ve více testovacích souborech, potom to vždy skončí chybou s tím, že tabulka v databázi neexistuje. Předpokládám, že Tester asi spouští testy paralelně, jiná příčina mě zatím nenapadla. Existuje nějaký způsob jak toto chování potlačit nebo jinak docílit toho, co jsem popsal?
Díky
- Filip Procházka
- Moderator | 4668
Databázové testy by jsi měl serializovat pomocí zámku
class SettingTest extends TestCase
{
private $container;
private $setting;
public function __construct(Container $container)
{
$this->container = $container;
}
protected function setUp()
{
Tester\Helpers::lock('db', dirname(TEMP_DIR));
$this->setting = new Setting($container);
$this->container->getService('database')
->loadFile(__DIR__.'/../initialization.sql');
}
public function testMethod1...() {}
- llook
- Člen | 407
Taky lze pro každý test vytvářet v setUp samostatnou databázi a v tearDown ji dropnout. Takhle to dělám s Dibi (s NDB to půjde velmi podobně):
for ($i = 1; ; $i++)
{
$dbName = $dbNamePrefix . $i;
try
{
$connection->query('CREATE DATABASE %n', $dbName, ' CHARSET=utf8 COLLATE=utf8_czech_ci');
$connection->query('USE %n', $dbName);
return $dbName;
}
catch (DibiException $e)
{
}
}
Použití zámku na databázové testy ale bude výkonnější.
- radas
- Člen | 224
@Filip Procházka: Díky, se zámkem to funguje. Jelikož mi stačí pro celý jeden test (třídu) inicializovat databázi jednou, mohlo by stačit toto?
public function __construct(Container $container)
{
...
Tester\Helpers::lock('initialization', TEMP_DIR);
$this->container->getService('database')->loadFile(__DIR__.'/../initialization.sql');
}
S tím, že pak pro každý jednotlivý test ve třídě by byl další zámek pro samotné dotazování do databáze:
protected function setUp()
{
parent::setUp();
Helpers::lock('db', TEMP_DIR);
$this->setting = new Setting($this->container);
}
Dávají takhle zámky smysl? Díky.
Editoval radas (3. 5. 2013 14:14)
- Filip Procházka
- Moderator | 4668
@radas jenže pokud testy pouštíš přes tester, tak se pro každou testovací metodu spustí samostatné vlákno. Tím že tu inicializaci oddělíš do konstruktoru nic moc nezískáš, imho to bude jenom méně přehledné (srovnej moje a tvoje).
- radas
- Člen | 224
@Filip Procházka: Pominu-li to, že inicializace DB, pokud je v metodě setUp(), způsobí delší běhu testu (nevadí), tak mi ale testy stejně neprojdou a skončí stejnou chybou jako jsem psal v prvním postu (tabulka neexistuje). Asi to záleží na tom, jak je nastavená konstanta TEMP_DIR, u mě
define('TEMP_DIR', __DIR__.'/../tmp/'.getmypid());
Což znamená, že se zámek uloží do adresáře s aktuálním vláknem/testem. Nemělo by to být tak, že by se zámek měl ukládat mimo adresáře, které vznikají pro jednotlivé procesy? V takovém případě testy prošly.
- Filip Procházka
- Moderator | 4668
Ano, máš pravdu, mělo by tam být něco jako
Tester\Helpers::lock('db', dirname(TEMP_DIR));