3. diel - Testovanie v PHP - Dokončenie unit testov
V minulej lekcii, Úvod do unit testov v PHP a inštalácia PHPUnit , sme si nainštalovali framework Codeception a vygenerovali svoj prvý PHPUnit test. Dnes pokryjeme testy našu jednoduchú triedu, uvedieme si dostupné asserční metódy a naše unit testy v PHP dokončíme.
Pokrytie triedy testy
Metódy _before()
(starší názov bol setUp()
) a
_after()
(starší názov bol tearDown()
) sa zavolajú
pred, resp. po každom teste v tejto triede. To je pre nás
veľmi dôležité, pretože podľa best practices chceme, aby boli testy
nezávislé. Obvykle teda pred každým testom pripravujeme
znovu to isté prostredie, aby sa vzájomne vôbec neovplyvňovali. O dobrých
praktikách sa zmienime detailnejšie neskôr. Do triedy si pridajme atribút
$kalkulacka
a v metóde _before()
v ňom vždy
vytvorme čerstvo novú kalkulačku pre každý test. Pokiaľ by ju bolo ešte
potrebné ďalej nastavovať alebo bolo treba vytvoriť ďalšie závislosti,
boli by tiež v tejto metóde. Metódu testSomeFeature()
odstránime:
class KalkulackaTest extends \Codeception\Test\Unit { /** * @var \UnitTester */ protected $tester; private $kalkulacka; protected function _before() { $this->kalkulacka = new Kalkulacka(); } protected function _after() { } }
Máme všetko pripravené na pridávanie samotných testov. Jednotlivé
metódy budú vždy začínať na "test" a budú testovať jednu konkrétnu
metódu z triedy Kalkulacka
, typicky pre niekoľko rôznych
vstupov. Ak vás napadá prečo metódy označujeme prefixy, umožňuje nám to
vytvoriť si aj pomocné metódy, ktoré môžeme v danom teste využívať a
ktoré nebudú považované za testy. PhpStorm nám totiž testy (metódy
začínajúce na "test") automaticky spustí a vypíše ich výsledky.
Pridajme nasledujúcich 5 metód:
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ávanie výstupu metódy s očakávanou hodnotou používame
poděděné metódy Assert *. Dajú sa volať aj staticky, ale my zostaneme u
inštančného použitie. Najčastejšie asi použijete
assertEquals()
, ktorá prijíma ako prvý parameter očakávanú
hodnotu a ako druhý parameter hodnotu aktuálny. Toto poradie je dobré
dodržiavať, inak budete mať hodnoty vo výsledkoch testov opačne. Ako asi
viete, desatinné čísla sú v pamäti počítača reprezentovaná binárne
(ako inak ) A to spôsobí
určitú stratu ich presnosti a tiež určité ťažkosti pri ich porovnávaní.
Preto musíme v tomto prípade zadať aj štvrtý parameter a to je
delta, teda kladná tolerancia, o koľko sa môže očakávaná
a aktuálna hodnota líšiť, aby test stále prešiel. Tretím parametrom je
chybová hláška, ak test neprebehne. Väčšinou ju nie je dôvod zadávať,
ak je test dobre pomenovaný az hodnôt jednoducho spoznáme, ktorý Assert sa
nepodaril.
Všimnite si, že skúšame rôzne vstupy. Sčítanie netestujeme len ako 1 + 1 = 2, ale skúsime celočíselné, desatinné aj negatívne vstupy, oddelene, a overíme výsledky. V niektorých prípadoch by nás mohla zaujímať tiež maximálna hodnota dátových typov a podobne.
Posledný test overuje, či metóda vydel()
naozaj vyvolá
výnimku pri nulovom deliteľmi. Ako vidíte, nemusíme sa zaťažovať s
try-catch bloky, stačí nad metódu pridať PHP anotáciu
@expectedException
a uviesť tu triedu výnimky, ktorá sa
očakáva. Ak výnimka nenastane, test zlyhá. Pre testovanie viac prípadov
vyvolanie výnimky týmto spôsobom by bolo treba pridať viac metód.
Dostupné Assert metódy
Okrem metódy assertEquals()
môžeme použiť ešte mnoho
ďalších, určite sa snažte použiť tú najviac vyhovujúce metódu,
sprehľadňuje to hlášky pri zlyhaní testov a samozrejme aj následnú
opravu. Zoznam Assert metód je pomerne vyčerpávajúce a môžete si ho
jednoducho prezrieť v IDE, spomeňme si teda len tie najdôležitejšie:
- assertContains ($ ihla, $ kôpka) - Skontroluje, či $ kôpka (pole) obsahuje danú hodnotu ($ ihla).
- assertCount ($ ocekavanyPocet, $ kolekcia) - Skontroluje, či má $ kolekcie $ ocekavanyPocet prvkov.
- assertFalse ($ hodnota) - Skontroluje, či je hodnota
false
. - assertTrue ($ hodnota) - Skontroluje, či je hodnota
true
. - assertNotEquals ($ ocekavanaHodnota, $ hodnota) - Skontroluje, či hodnoty nie sú rovnaké. Podobná "Not" metóda je pre väčšinu Assert, ďalšie tu už nebudeme zbytočne zmieňovať.
- assertGreaterThan ($ ocekavanaHodnota, $ hodnota) - Skontroluje, či je $ hodnota väčšia ako $ ocekavanaHodnota.
- assertGreaterThanOrEqual ($ ocekavanaHodnota, $ hodnota) - Skontroluje, či je $ hodnota väčšia alebo rovná $ ocekavanaHodnota.
- assertLessThan ($ ocekavanaHodnota, $ hodnota) - Skontroluje, či je $ hodnota menšia ako $ ocekavanaHodnota.
- assertLessThanOrEqual ($ ocekavanaHodnota, $ hodnota) - Skontroluje, či je $ hodnota menšia alebo rovná $ ocekavanaHodnota.
- assertNull ($ hodnota) - Skontroluje, či je hodnota
null
. - assertSame ($ ocekavanaHodnota, $ hodnota) - Funguje
rovnako ako
assertEquals()
, ale kontroluje aj zhodu dátových typov.
Sú tu aj pripravené Assert pre testovanie atribútov, reťazcov (napr. Či niečím začína), polí, priečinkov, súborov a XML.
AssertThat ()
Veľmi zaujímavá je ešte metóda assertThat()
, ktorá
umožňuje alternatívny prístup k assercím. Napr. v Jave (PHPUnit pomerne
jasne vychádza z JUnit) tento spôsob prináša navyše ďalšie možnosti
kontroly dátových typov, v PHP si ho spomeňme skôr len pre zaujímavosť a
ukážme si, ako by vyzeral prvý Assert z našich testov pomocou
assertThat()
. Pripomeňme si pôvodný variant:
$this->assertEquals(2, $this->kalkulacka->secti(1, 1));
A verzie s assertThat()
:
$this->assertThat( $this->kalkulacka->secti(1, 1), $this->equalTo( 2 ) );
Výhodou je, že zápis vyzerá ako anglická veta. Nevýhodou je vyšší objem kódu a rekurzívne ponáranie. Pre zložitejšie Assert môže byť tento spôsob výhodný.
Spustenie testov
Testy spustíme príkazom:
test run unit
Uvidíme výsledky, ktoré vyzerajú nejako takto:
> C:\xampp\php\php.exe codecept.phar run unit Codeception PHP Testing Framework v2.3.8 Powered by PHPUnit 6.5.6 by Sebastian Bergmann and contributors. Unit Tests (5) ------------------- + KalkulackaTest: Scitani (0.01s) + KalkulackaTest: Odcitani (0.00s) + KalkulackaTest: Nasobeni (0.00s) + KalkulackaTest: Deleni (0.00s) + KalkulackaTest: Deleni vyjimka (0.00s) ---------------------------------- Time: 314 ms, Memory: 8.00MB OK (5 tests, 13 assertions) Process finished with exit code 0 at 19:32:44. Execution time: 467 ms.
Ak vám Codeception hlási problém s nedostupnosťou príkazu "php", otvorte
konfiguračný súbor codeception.yml a do sekcie settings pridajte hodnotu
lint: false
.
... settings: bootstrap: _bootstrap.php colors: false memory_limit: 1024M lint: false ...
V určitých verziách sa inak môže zle vyhodnotiť výstupný stav.
Skúsme si teraz urobiť v kalkulačke chybu, napr. Zakomentujte vyvolávanie výnimky pri delení nulou a vráťme vždy hodnotu 1:
public function vydel($a, $b) { //if ($b == 0) // throw new \InvalidArgumentException("Nelze dělit nulou!"); return 1; }
A spustite znovu naše testy:
> C:\xampp\php\php.exe codecept.phar run unit Codeception PHP Testing Framework v2.3.8 Powered by PHPUnit 6.5.6-2 by Sebastian Bergmann and contributors. Unit Tests (5) ------------------- + KalkulackaTest: Scitani (0.01s) + KalkulackaTest: Odcitani (0.00s) + KalkulackaTest: Nasobeni (0.00s) x KalkulackaTest: Deleni (0.00s) x KalkulackaTest: Deleni vyjimka (0.00s) ---------------------------------- Time: 334 ms, Memory: 8.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:44 #2 KalkulackaTest->testDeleni --------- 2) KalkulackaTest: Deleni vyjimka Test tests\unit\KalkulackaTest.php:testDeleniVyjimka Failed asserting that exception of type "InvalidArgumentException" is thrown. FAILURES! Tests: 5, Assertions: 11, Failures: 2. Process finished with exit code 1 at 19:35:07. Execution time: 490 ms.
Vidíme, že chyba je zachytená a sme na ňu upozornení. Neprešiel ako test delenia, tak test vyvolanie výnimky. Môžeme kód vrátiť späť do pôvodného stavu.
V budúcej lekcii, PHPUnit DataProvider a BestPractices , si ukážeme vybrané zdrojové kódy zaujímavých unit testov komerčných systémov, aby ste získali prehľad ako testovať zložitejšie situácie.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 76x (1.29 MB)
Aplikácia je vrátane zdrojových kódov v jazyku PHP