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

Odebírat
Upozornit na
guest
0 Komentářů
Nejstarší
Nejnovější Nejvíce hlasů

Proč vám model napíše Python, i když jste si řekli o Rust

AI
Komentáře: 0
Výkonnostní propast mezi Pythonem a ostatními jazyky se za poslední rok a půl skoro zavřela. Dnešní modely zvládají Rust i Go srovnatelně dobře. Když jim ale volbu necháte, sáhnou stejně po Pythonu. A u knihoven je to ještě výraznější: mezi funkčně srovnatelnými balíčky je až 84procentní rozdíl v kvalitě generovaného kódu. Proč to tak je a co s tím.

WP2Shell: Kritická hrozba pro samotné jádro WordPressu. Útočníci mohou získat kontrolu nad webem

Zranitelnost ve WordPressu není žádná novinka. Kdo provozuje weby postavené na této platformě, ví, že bezpečnostní záplaty chodí prakticky pořád. O to větší pozornost by měla vzbudit chyba, u které nic z toho neplatí. A přesně takový je případ zranitelnosti, která dostala přezdívku wp2shell. potřeb. Zranitelný kontaktní formulář, děravý e-shopový plugin, opomenutá kontrola oprávnění v nějaké obskurní rozšiřující knihovně – to je denní chleba každého, kdo sleduje bezpečnostní feedy. Zpráva „nová chyba ve WordPress pluginu“ má tak nízkou informační hodnotu, že ji většina lidí přejde bez mrknutí oka.

Mýtus jedné aplikace: proč PWA nenahradí vývoj pro každou platformu

PWA mohou webu přidat ikonu na ploše, fungování bez připojení, notifikace a některé systémové funkce. Nejsou ale cestou k jednomu klientu pro všechny platformy. Vyplatí se tam, kde se lidé k webu vracejí a ocení okamžitý vstup z odkazu. Jakmile aplikace musí spolehlivě běžet na pozadí nebo fungovat stejně na každém zařízení, bývá vhodnější nativní řešení.