Testování v PHP: testy integrace s databází II.

V minulém díle jsme se seznámili se základy testování integrace s databází pomocí rozšíření DbUnit, dnes se podíváme na jeho další možnosti.

Seriál: Testování a tvorba testovatelného kódu v PHP (13 dílů)

  1. Testování a tvorba testovatelného kódu v PHP 13.8.2012
  2. Testování v PHP: Instalace a základy PHPUnit 27.8.2012
  3. Testování v PHP: asserty a constraints 10.9.2012
  4. Testování v PHP: praktický příklad 1.10.2012
  5. Testování v PHP: anotace 8.10.2012
  6. Testování v PHP: odstiňujeme závislosti 22.10.2012
  7. Testování v PHP: odstiňujeme závislosti II. 5.11.2012
  8. Testování v PHP: testy integrace s databází 19.11.2012
  9. Testování v PHP: testy integrace s databází II. 3.12.2012
  10. Testování v PHP: řízení běhu pomocí parametrů 7.1.2013
  11. Testování v PHP: XML konfigurace PHPUnit 21.1.2013
  12. Testování v PHP: tvorba testovatelného kódu 18.2.2013
  13. Testování v PHP: tvorba testovatelného kódu II. 11.3.2013

Abstraktní předek pro integrační testy

Ještě než začneme, tak si připravíme abstraktního předka, od kterého budou všechny naše integrační testy dědit. Důvod je prostý – nechceme dokola opisovat stále stejné metody pro přípravu fixture, což svádí ke copy-paste a to je téměř vždy cesta do pekel. Pokud budeme potřebovat pro nějakou sadu testů jiný inicializační dataset, pak jednoduše překryjeme metodu getDataset.

Máme-li větší množství integračních testů, můžeme narazit na problém s příliš velkým počtem otevřených spojení s databází. Tomuto je možné čelit dvěma způsoby. Buď budeme spojení vždy zavírat v metodě tearDown, nebo si připojení uložíme do statické property abstraktní třídy DatabaseTestCase.

Obecně uznávané, nebo spíše tolerované, řešení je pomocí statické property. Získáme tím sice jedno připojení pro všechny testy, ale na druhou stranu pošlapeme jedno ze základních pravidel dobrých testů – nezávislost. Opravdu není dobrým zvykem v testech cokoli sdílet.

Možná vás napadne – proč tedy ukládat instanci DefaultDBConnection, před každým test case bude přece vytvořena znovu? Odpověď je nasnadě – instanci DefaultDBConnection (tzv. Connection API) budeme potřebovat i v dynamických datasetech, které si ukážeme už za malou chvilku.

abstract class DatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
{
    /**
     * @var PHPUnit_Extensions_Database_DB_IDatabaseConnection
     */
    protected $connection = null;

    /**
     * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
     */
    protected function getConnection()
    {
        if ($this->connection === null) {
            $this->connection = $this->createDefaultDBConnection(
                new PDO('mysql:host=localhost;dbname=test', 'test', 'test')
            );
        }

        return $this->connection;
    }

    /**
     * @return PHPUnit_Extensions_Database_DataSet_IDataSet
     */
    protected function getDataset()
    {
        return $this->createFlatXMLDataSet(__DIR__ . "/dataset.xml");
    }

    protected function tearDown()
    {
        $this->connection->close();
        parent::tearDown();
    }
}

Dynamické datasety

Kromě statických datasetů, které jsme si minule ukázali, poskytuje DbUnit i dynamické, které můžeme vytvářet za běhu, nad aktuálním připojením k databázi. K čemu může být toto vhodné? Např. pokud chceme ověřovat výsledky dotazů, obsahy celých tabulek apod.

Database dataset

Tento dataset můžeme vytvořit dvěma způsoby: buď zavoláním metody createDataSet, kterou nabízí tzv. Connection API nebo přímo – vytvořením instance třídy PHPUnit_Extensions_Databa­se_DB_DataSet, které jako parametr konstruktoru předáme instanci Connection API.

Metoda createDataSet obsahuje jeden nepovinný parametr – pole se seznamem tabulek, které chceme do datasetu zahrnout. Defaultně jsou zahrnuty všechny, tímto parametrem dataset pouze omezujeme.

class DatabaseDatasetTest extends DatabaseTestCase
{
    public function testDatabaseDataset()
    {
        $expectedDataSet = $this->createFlatXMLDataSet(
            __DIR__ . "/databaseDataset.xml");
        $dbDataSet = $this->getConnection()->createDataSet();

        $this->assertDataSetsEqual($expectedDataSet, $dbDataSet);
    }

    public function testFilteredDatabaseDataset()
    {
        $expectedDataSet = $this->createFlatXMLDataSet(
            __DIR__ . "/databaseDataset.xml");
        $dbDataSet = $this->getConnection()->createDataSet(array("product"));

        $this->assertDataSetsEqual($expectedDataSet, $dbDataSet);
    }
}

Tento příklad nedělá nic světoborného. V prvním případě pouze ověřuje, zda obsah databáze (naplněný v rámci přípravy fixture) skutečně odpovídá obsahu datasetu, definovaného v souboru dataset.xml. Ve druhém případě to samé, jen s tím rozdílem, že dataset omezíme na tabulku product.

Query table dataset

Query table dataset je podobný předchozímu, jen s tím rozdílem, že jej můžeme plnit SQL dotazem. Využití nalézá třeba tam, kde chceme ověřovat výsledek dotazu proti námi definovanému datasetu. Vytvořit jej můžeme opět dvěma způsoby – buď pomocí metody createQueryTable nad Connection API nebo vytvořením instance třídy PHPUnit_Extensions_Databa­se_DataSet_QueryTable. Metoda createQueryTable přijímá dva parametry – název výsledku a SQL dotaz.

class QueryTableDatasetTest extends DatabaseTestCase
{
    public function testQueryTableDataset()
    {
        $queryTableDataset = $this->getConnection()->createQueryTable(
            "product", "SELECT * FROM product WHERE id > 1");

        $expectedDataSet = $this->createFlatXMLDataSet(
            __DIR__ . "/queryResultDataset.xml")->getTable("product");

        $this->assertTablesEqual($expectedDataSet, $queryTableDataset);
    }
}

Query dataset

Poslední z dynamických datasetů. Lze si jej představit jako rozšířenou verzi předchozího – není omezen jen na jednu tabulku, do tohoto datasetu můžeme přidávat hned několik výsledků SQL dotazů.

class QueryDatasetTest extends DatabaseTestCase
{
    public function testQueryDataset()
    {
        $queryDataset = new PHPUnit_Extensions_Database_DataSet_QueryDataSet($this->getConnection());
        $queryDataset->addTable("product", "SELECT * FROM product WHERE id > 1");
        // ... more SQL queries ...

        $expectedDataSet = $this->createFlatXMLDataSet(
            __DIR__ . "/queryResultDataset.xml");

        $this->assertDataSetsEqual($expectedDataSet, $queryDataset);
    }
}

Nové asserty

Rozšíření DbUnit přináší také další čtyři asserty, které můžeme použít.

assertTablesEqual

assertTablesEqual(PHPUnit_Extensions_Database_DataSet_ITable $expected, PHPUnit_Extensions_Database_DataSet_ITable $actual, $message = '')

Jak už název napovídá, assert ověřuje, zda se očekávaný obsah tabulky shoduje s aktuálním. Příklad použití už jsme viděli v ukázce Query table dataset.

assertDataSetsEqual

assertDataSetsEqual(PHPUnit_Extensions_Database_DataSet_IDataSet $expected, PHPUnit_Extensions_Database_DataSet_IDataSet $actual, $message = '')

Ověřuje shodu dvou celých datasetů. Příklad použití už jsme také viděli – viz ukázky Database dataset a Query dataset.

assertTableRowCount

assertTableRowCount($tableName, $expected, $message = '')

Jednoduchý assert ověřující, zda počet řádků v tabulce odpovídá očekávanému počtu. U tohoto assertu pozor na verzi rozšíření DbUnit, v aktuální verzi je chyba! Viz issue: https://github.com/se­bastianbergmann/dbunit/pu­ll/78.

class AssertTableRowCountTest extends DatabaseDatasetTest
{
    public function testAssertTableRowCount()
    {
        $this->assertTableRowCount("product", 3);
    }
}

assertTableContains

assertTableContains(array $expectedRow, PHPUnit_Extensions_Database_DataSet_ITable $table, $message = '')

Poslední z nových assertů – ověřuje existenci řádku v tabulce. Hledaný řádek definujeme pomocí pole.

class AssertTableContainsTest extends DatabaseTestCase
{
    public function testAssertTableContains()
    {
        $expected = array(
            "id" => "1",
            "name" => "Test product 1",
            "price" => "123");

        $this->assertTableContains(
            $expected,
            $this->getConnection()->createDataSet()->getTable("product"));
    }
}

Příště

Jako obvykle, všechny zdrojové kódy naleznete na Githubu, můžete si tak vše vyzkoušet. Dnešní díl je kratší než bývá zvykem, příště se podíváme na další velkou kapitolu frameworku PHPUnit, a tou bude XML konfigurace a CLI parametry, kterými můžeme řídit běh testů.

Josef Zamrzla pracuje jako nezávislý vývojář. Před tím působil coby software development engineer ve společnosti Skype, programátor ve společnosti LMC s.r.o. (provozovatel pracovních portálů www.jobs.cz a www.prace.cz) nebo teamleader ve společnosti Kasa.cz 

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Zatím nebyl přidán žádný komentář, buďte první!

Přidat komentář
Zdroj: https://www.zdrojak.cz/?p=3748