Přejít k navigační liště

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

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

Články PHP, Různé

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.

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ů.

Komentáře

Subscribe
Upozornit na
guest
0 Komentářů
Inline Feedbacks
View all comments

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.