Přidej si svou IT školu do profilu a najdi spolužáky zde na síti :)

3. díl - Testování v PHP - Dokončení unit testů

PHP Testování Testování v PHP - Dokončení unit testů

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci o testování webových aplikací v PHP jsme si nainstalovali framework Codeception a vygenerovali svůj první PHPUnit test. Dnes pokryjeme testy naši jednoduchou třídu, uvedeme si dostupné asserční metody a naše unit testy v PHP dokončíme.

Pokrytí třídy testy

Metody setUp() a tearDown() se zavolají před, resp. po každém testu v této třídě. To je pro nás velmi důležité, jelikož podle best practices chceme, aby byly testy nezávislé. Obvykle tedy před každým testem připravujeme znovu to samé prostředí, aby se vzájemně vůbec neovlivňovaly. O dobrých praktikách se zmíníme detailněji později. Do třídy si přidejme atribut $kalkulacka a v metodě setUp() v něm vždy vytvořme čerstvě novou kalkulačku pro každý test. Pokud by ji bylo ještě třeba dále nastavovat nebo bylo třeba vytvořit další závislosti, byly by také v této metodě. Metodu testMe() odstraníme:

class KalkulackaTest extends \PHPUnit_Framework_TestCase
{
    private $kalkulacka;

    protected function setUp()
    {
        $this->kalkulacka = new Kalkulacka();
    }

    protected function tearDown()
    {
    }

}

Máme vše připraveno k přidávání samotných testů. Jednotlivé metody budou vždy začínat na "test" a budou testovat jednu konkrétní metodu z třídy Kalkulacka, typicky pro několik různých vstupů. Pokud vás napadá proč metody označujeme prefixy, umožňuje nám to vytvořit si i pomocné metody, které můžeme v daném testu využívat a které nebudou pokládány za testy. PhpStorm nám totiž testy (metody začínající na "test") automaticky spustí a vypíše jejich výsledky.

Přidejme následujících 5 metod:

public function testScitani()
{
        $this->assertEquals(2, $this->kalkulacka->secti(1, 1));
        $this->assertEquals(1.42, $this->kalkulacka->secti(3.14, -1.72), '', 0.001);
        $this->assertEquals(2/3, $this->kalkulacka->secti(1/3, 1/3), '', 0.001);
}

public function testOdcitani()
{
        $this->assertEquals(0, $this->kalkulacka->odecti(1, 1));
        $this->assertEquals(4.86, $this->kalkulacka->odecti(3.14, -1.72), '', 0.001);
        $this->assertEquals(2/3, $this->kalkulacka->odecti(1/3, -1/3), '', 0.001);
}

public function testNasobeni()
{
        $this->assertEquals(2, $this->kalkulacka->vynasob(1, 2));
        $this->assertEquals(-5.4008, $this->kalkulacka->vynasob(3.14, -1.72), '', 0.001);
        $this->assertEquals(0.111, $this->kalkulacka->vynasob(1/3, 1/3), '', 0.001);
}

public function testDeleni()
{
        $this->assertEquals(2, $this->kalkulacka->vydel(4, 2));
        $this->assertEquals(-1.826, $this->kalkulacka->vydel(3.14, -1.72), '', 0.001);
        $this->assertEquals(1, $this->kalkulacka->vydel(1/3, 1/3));
}

/**
 * @expectedException InvalidArgumentException
 */
public function testDeleniVyjimka()
{
        $this->kalkulacka->vydel(2, 0);
}

K porovnávání výstupu metody s očekávanou hodnotou používáme poděděné metody assert*. Dají se volat i staticky, ale my zůstaneme u instančního použití. Nejčastěji asi použijete assertEquals(), která přijímá jako první parametr očekávanou hodnotu a jako druhý parametr hodnotu aktuální. Toto pořadí je dobré dodržovat, jinak budete mít hodnoty ve výsledcích testů opačně. Jak asi víte, desetinná čísla jsou v paměti počítače reprezentována binárně (jak jinak :) ) a to způsobí určitou ztrátu jejich přesnosti a také určité obtíže při jejich porovnávání. Proto musíme v tomto případě zadat i čtvrtý parametr a to je delta, tedy kladná tolerance, o kolik se může očekávaná a aktuální hodnota lišit, aby test stále prošel. Třetím parametrem je chybová hláška, pokud test neproběhne. Většinou ji není důvod zadávat, pokud je test dobře pojmenovaný a z hodnot jednoduše poznáme, který assert se nepovedl.

Všimněte si, že zkoušíme různé vstupy. Sčítání netestujeme jen jako 1 + 1 = 2, ale zkusíme celočíselné, desetinné i negativní vstupy, odděleně, a ověříme výsledky. V některých případech by nás mohla zajímat také maximální hodnota datových typů a podobně.

Poslední test ověřuje, zda metoda vydel() opravdu vyvolá výjimku při nulovém děliteli. Jak vidíte, nemusíme se zatěžovat s try-catch bloky, stačí nad metodu přidat PHP anotaci @expectedException a uvést zde třídu výjimky, která se očekává. Pokud výjimka nenastane, test selže. Pro testování více případů vyvolání výjimky tímto způsobem by bylo třeba přidat více metod.

Dostupné assert metody

Kromě metody assertEquals() můžeme použít ještě mnoho dalších, určitě se snažte použít tu nejvíce vyhovující metodu, zpřehledňuje to hlášky při selhání testů a samozřejmě i následnou opravu. Seznam assert metod je poměrně vyčerpávající a můžete si jej jednoduše prohlédnout v IDE, zmiňme si tedy jen ty nejdůležitější:

  • assertContain­s($jehla, $kupka) - Zkontroluje, zda $kupka (pole) obsahuje danou hodnotu ($jehla).
  • assertCount($o­cekavanyPocet, $kolekce) - Zkontroluje, zda má $kolekce $ocekavanyPocet prvků.
  • assertFalse($hod­nota) - Zkontroluje, zda je hodnota false.
  • assertTrue($hod­nota) - Zkontroluje, zda je hodnota true.
  • assertNotEqual­s($ocekavanaHod­nota, $hodnota) - Zkontroluje, zda hodnoty NEjsou stejné. Podobná "Not" metoda je pro většinu assertů, další zde již nebudeme zbytečně zmiňovat.
  • assertGreater­Than($ocekava­naHodnota, $hodnota) - Zkontroluje, zda je $hodnota větší než $ocekavanaHodnota.
  • assertGreater­ThanOrEqual($o­cekavanaHodno­ta, $hodnota) - Zkontroluje, zda je $hodnota větší nebo rovna $ocekavanaHodnota.
  • assertLessThan($o­cekavanaHodno­ta, $hodnota) - Zkontroluje, zda je $hodnota menší než $ocekavanaHodnota.
  • assertLessTha­nOrEqual($oce­kavanaHodnota, $hodnota) - Zkontroluje, zda je $hodnota menší nebo rovna $ocekavanaHodnota.
  • assertNull($hod­nota) - Zkontroluje, zda je hodnota null.
  • assertSame($o­cekavanaHodno­ta, $hodnota) - Funguje stejně jako assertEquals(), ale kontroluje i shodu datových typů.

Jsou zde i připravené asserty pro testování atributů, řetězců (např. zda něčím začíná), polí, složek, souborů a XML.

assertThat()

Velmi zajímavá je ještě metoda assertThat(), která umožňuje alternativní přístup k assercím. Např. v Javě (PHPUnit poměrně jasně vychází z JUnit) tento způsob přináší navíc další možnosti kontroly datových typů, v PHP si jej zmiňme spíše jen pro zajímavost a ukažme si, jak by vypadal první assert z našich testů pomocí assertThat(). Připomeňme si původní variantu:

$this->assertEquals(2, $this->kalkulacka->secti(1, 1));

A verze s assertThat():

$this->assertThat(
        $this->kalkulacka->secti(1, 1),
        $this->equalTo(
                2
        )
);

Výhodou je, že zápis vypadá jako anglická věta. Nevýhodou je vyšší objem kódu a rekurzivní zanořování. Pro složitější asserty může být tento způsob výhodný.

Spuštění testů

Testy spustíme příkazem:

test run unit

Uvidíme výsledky, které vypadají nějak takto:

> C:\xampp\php\php.exe codecept.phar run unit
Codeception PHP Testing Framework v2.2.10
Powered by PHPUnit 5.7.17 by Sebastian Bergmann and contributors.

Unit Tests (5) -----------------------------------------------------------------
+ KalkulackaTest: Scitani (0.00s)
+ KalkulackaTest: Odcitani (0.00s)
+ KalkulackaTest: Nasobeni (0.00s)
+ KalkulackaTest: Deleni (0.01s)
+ KalkulackaTest: Deleni vyjimka (0.00s)
--------------------------------------------------------------------------------


Time: 254 ms, Memory: 11.00MB

OK (5 tests, 13 assertions)

Process finished with exit code 0 at 02:11:58.
Execution time: 328 ms.

Pokud vám Codeception hlásí problém s nedostupností příkazu "php", otevřete konfigurační soubor codeception.yml a do sekce settings přidejte hodnotu lint: false.

...
settings:
    bootstrap: _bootstrap.php
    colors: false
    memory_limit: 1024M
    lint: false
...

V určitých verzích se jinak může špatně vyhodnotit výstupní stav.

Zkusme si nyní udělat v kalkulačce chybu, např. zakomentujme vyvolávání výjimky při dělení nulou a vraťme vždy hodnotu 1:

public function vydel($a, $b)
{
        //if ($b == 0)
        //      throw new \InvalidArgumentException("Nelze dělit nulou!");
        return 1;
}

A spusťme znovu naše testy:

> C:\xampp\php\php.exe codecept.phar run unit
Codeception PHP Testing Framework v2.2.10
Powered by PHPUnit 5.7.17 by Sebastian Bergmann and contributors.

Unit Tests (5) -----------------------------------------------------------------
+ KalkulackaTest: Scitani (0.00s)
+ KalkulackaTest: Odcitani (0.00s)
+ KalkulackaTest: Nasobeni (0.00s)
x KalkulackaTest: Deleni (0.01s)
x KalkulackaTest: Deleni vyjimka (0.00s)
--------------------------------------------------------------------------------


Time: 252 ms, Memory: 11.00MB

There were 2 failures:

---------
1) KalkulackaTest: Deleni
 Test  tests\unit\KalkulackaTest.php:testDeleni
Failed asserting that 1 matches expected 2.
#1  C:\Users\David\PhpstormProjects\kalkulacka\tests\unit\KalkulackaTest.php:39
#2  KalkulackaTest->testDeleni
#3  C:\Users\David\PhpstormProjects\kalkulacka\codecept.phar:7

---------
2) KalkulackaTest: Deleni vyjimka
 Test  tests\unit\KalkulackaTest.php:testDeleniVyjimka
Failed asserting that exception of type "InvalidArgumentException" is thrown.
#1  C:\Users\David\PhpstormProjects\kalkulacka\codecept.phar:7

FAILURES!
Tests: 5, Assertions: 11, Failures: 2.

Process finished with exit code 1 at 02:51:07.
Execution time: 312 ms.

Vidíme, že chyba je zachycena a jsme na ni upozorněni. Neprošel jak test dělení, tak test vyvolání výjimky. Můžeme kód vrátit zpět do původního stavu.

V příští lekci, PHPUnit DataProvider a BestPractices, si ukážeme vybrané zdrojové kódy zajímavých unit testů komerčních systémů, abyste získali přehled jak testovat složitější situace.


 

Stáhnout

Staženo 8x (2.89 MB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
4 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Miniatura
Všechny články v sekci
Testování v PHP
Miniatura
Následující článek
PHPUnit DataProvider a BestPractices
Aktivity (5)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!