IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

2. diel - Úvod do unit testov v PHP a inštalácia PHPUnit

V minulej lekcii, Úvod do testovania webových aplikácií v PHP , sme si urobili pomerne solídny úvod do problematiky. Tiež sme si uviedli v-model, ktorý znázorňuje vzťah medzi jednotlivými výstupmi fázou návrhu a príslušnými testami.

Testy teda píšeme vždy na základe návrhu, nie implementácie. Inými slovami, robíme je na základe očakávanej funkčnosti. Tá môže byť buď priamo od zákazníka (a to v prípade akceptačných testov) alebo už od programátora (architekta), kde špecifikuje ako sa má ktorá metóda správať. Dnes sa budeme venovať práve týmto testom, ktorým hovoríme jednotkové (unit testy) a ktoré testujú detailnú špecifikáciu aplikácie, teda jej triedy.

Pamätajte, že nikdy nepíšeme testy podľa toho, ako je niečo vnútri naprogramované! Veľmi jednoducho by to mohlo naše myslenie zviesť len tým daným spôsobom a zabudli by sme na to, že metóde môžu prísť napríklad aj iné vstupy, na ktoré nie je vôbec pripravená. Testovanie s implementáciou v skutočnosti vôbec nesúvisí, vždy testujeme či je splnené zadanie.

Aké triedy testujeme

Unit testy testujú jednotlivé metódy v triedach. Pre istotu zopakujem, že nemá veľký zmysel testovať jednoúčelové metódy napr. V modeloch, ktoré napr. Len niečo vyberajú z databázy. Aby sme boli konkrétnejší, nemá zmysel testovať metódu ako je táto:

public function vlozPolozku($nazev, $cena)
{
    $this->db->dotaz("INSERT INTO polozka (nazev, cena) VALUES (?, ?)", $nazev, $cena);
}

Metóda pridáva položku do databázy. Typicky je použitá len v nejakom formulári a ak by nefungovala, zistí to akceptačné testy, pretože by sa nová položka neobjavila v zozname. Podobných metód je v aplikácii veľa a zbytočne by sme strácali čas pokrývaním niečoho, čo ľahko pokryjeme v iných testoch.

Unit testy nájdeme najčastejšie u knižníc, teda nástrojov, ktoré programátor používa na viacerých miestach alebo dokonca vo viacerých projektoch a mali by byť 100% funkčný. Možno si spomeniete, kedy ste použili nejakú knižnicu, stiahnutú napr. Z GitHub. Veľmi pravdepodobne u nej boli tiež testy, ktoré sa najčastejšie vkladajú do zložky "tests", ktorá je vedľa zložky "src" v adresárovej štruktúre projektu. Ak napr. Píšeme aplikáciu, v ktorej často potrebujeme nejaké matematické výpočty, napr. Faktoriál a ďalšie pravdepodobnostné funkcie, je samozrejmosťou vytvoriť si na tieto výpočty knižnicu a je veľmi dobrý nápad pokryť takú knižnicu testy.

Príklad

Ako asi tušíte, my si podobnú triedu vytvoríme a skúsime si ju otestovať. Aby sme sa nezdržovali, vytvorme si iba jednoduchú kalkulačku, ktorá bude vedieť:

  • sčítať
  • odčítať
  • násobiť
  • deliť

Vytvorenie projektu

V praxi by v triede boli nejaké zložitejšie výpočty, ale tým sa tu zaoberať nebudeme. Vytvorte si nový projekt s názvom kalkulacka a do neho si pridajte triedu Kalkulacka a nasledujúce implementácií:

<?php

/**
 * Reprezentuje jednoduchou kalkulačku
 */
class Kalkulacka
{

    /**
     * Sečte 2 čísla
     * @param int|float $a První číslo
     * @param int|float $b Druhé číslo
     * @return int|float Součet 2 čísel
     */
    public function secti($a, $b)
    {
        return $a + $b;
    }

    /**
     * Odečte 2 čísla
     * @param int|float $a První číslo
     * @param int|float $b Druhé číslo
     * @return int|float Rozdíl 2 čísel
     */
    public function odecti($a, $b)
    {
        return $a - $b;
    }

    /**
     * Vynásobí 2 čísla
     * @param int|float $a První číslo
     * @param int|float $b Druhé číslo
     * @return int|float Součin 2 čísel
     */
    public function vynasob($a, $b)
    {
        return $a * $b;
    }

    /**
     * Vydělí 2 čísla
     * @param int|float $a První číslo
     * @param int|float $b Druhé číslo
     * @return int|float Podíl 2 čísel
     */
    public function vydel($a, $b)
    {
        if ($b == 0)
            throw new \InvalidArgumentException("Nelze dělit nulou!");
        return $a / $b;
    }

}

Na kódu je zaujímavá iba metóda vydel(), ktorá vyvolá výnimku v prípade, že delíme nulou. Predvolené správanie PHP je chyba skriptu, čo by v aplikácii užívateľ vidieť nikdy nemal. Trieda by mohla byť pokojne aj v mennom priestore, v teste by sa potom importovali štandardne pomocou use.

PHPUnit

V PHP sa unit testy píšu najčastejšie v PHPUnit frameworku, ktorý by mal každý PHP programátor poznať. Existujú samozrejme alternatívne nástroje, napr. Nette tester, ktoré ale fungujú všetky veľmi podobne.

PHPUnit - Testovanie v PHP

Aj keď by sme mohli PHPUnit nainštalovať samostatne, ako my v kurze, tak vy u svojich aplikácií, budeme neskôr potrebovať aj ďalšie typy testov, aspoň tie akceptačné. Na tie je potrebné pracovať s ďalšími nástrojmi a inštalovať všetko zvlášť by dalo pomerne dosť práce. Preto PHPUnit nainštalujeme pomocou frameworku Codeception.

Codeception

Codeception je komplexný testovací framework pre PHP, ktorý obsahuje:

  • PHPUnit
  • Akceptačný wrapper pre Selenium
  • Ďalšie, pre nás nepodstatné testovacie frameworky

Môžeme ho nainštalovať buď cez composer, alebo jednoducho stiahnutím jediného súboru .phar. Ak ste o .phar súboroch ešte nepočuli, sú to spustiteľné archívy s PHP aplikáciami, ktoré možno spúšťať napr. Cez vaše IDE.

Inštalácia

Ja tu využijem prvú možnosť cez .phar súbor, riešenie cez composer je pre záujemcov nižšie. Prejdeme na QUICKSTART na adrese http://codeception.com/quickstart a stiahneme súbor codecept.phar, ideálne si ho uložte do priečinka s dnešným projektom.

Články sú testované v Codeception verzie 2.5.6. Túto verziu možno stiahnuť tu: https://codeception.com/builds (kliknutím na odkaz Download latest 2.5 Release).

Keďže testy sú už pokročilejšie tému, použijeme pre neho i pokročilejších IDE - PhpStorm. Samozrejme môžete testovať aj v NetBeans, ak z nejakého dôvodu chcete.

Teraz vytvoríme pre .phar súbor alias, aby sme ho mohli jednoducho spúšťať z konzoly. V aplikačnom menu File zvolíme Settings a do vyhľadávania hore napíšeme "Command Li", čo nám otvorí nástroj Command Line Tool Support.

Command Line Tools Support v PhpStorm - Testovanie v PHP

Pomocou tlačidla "+" vpravo hore pridáme novú položku typu "Custom tool" s viditeľnosťou pre projekt. Následne do formulára vyplníme:

  • Tool path: C:\xampp\php\php.exe codecept.phar, cestu k svojmu PHP interpreter si prípadne upravte. Na Linuxe stačí zadať miesto cesty len php.
  • Alias: test (to je názov príkazu, cez ktorý budeme archív spúšťať)
Pridanie Command Line Tool v PhpStorm - Testovanie v PHP

Všetky okná potvrďte.

Inštalácia cez Composer

Táto pasáž opisuje inštaláciu Codeception cez Composer. Pokiaľ ho nepoužívate, preskočte ju. Ak máte Composer, Codeception nainštalujete príkazom composer require "codeception/codeception" --dev

Články sú testované v Codeception verzie 2.5.6, čo bola posledná verzia v dobe ich publikácie. Túto špecifickú verziu možno stiahnuť príkazom: composer require "codeception/codeception:2.5.6".

Odporúčam si PhpStorm s Composer a Codeception prepojiť, inak pravdepodobne nebude fungovať napovedanie kódu.

IDE by sa malo nastaviť automaticky, však radšej prikladám screenshoty môjho nastavenia:

Nastavenie Composer v PhpStorm - Testovanie v PHP
Nastavenie Composer Autoloader v PhpStorm - Testovanie v PHP
Nastavenie Codeception v PhpStorm - Testovanie v PHP

V tomto prípade bude nastavená cesta pre príkaz Composer ako cesta k batch súboru vygenerovanom cez Composer. Mala by vyzerať takto: cesta_k_projektu\Kalkulacka\vendor\bin\codecept.bat. V prípade Linuxu použijete súbor bez koncovky .bat.

Bootstrap

Teraz otvoríme menu Tools -> Run command a do konzoly (pozor, nepliesť s terminálom, ten je v PhpStorm tiež) vložíme nasledujúci kód:

test bootstrap

Tým docielime vygenerovanie testov do nášho projektu.

Všimnite si, že sa v ňom objavila zložka tests, ktorá obsahuje niekoľko ďalších súborov a podpriečinkov. Pre nás bude zatiaľ dôležitá podzložka unit, do ktorej budeme generovať nové unit testy. Keďže testy používajú triedy z našej aplikácie, potrebujú definovať minimálne autoloader. To sa robí v súboroch _bootstrap.php, ktoré sú tu buď zvlášť pre každý typ testov alebo pre všetky testy.

My si v našom prípade vytvoríme súbor _bootstrap.php v priečinku unit, kde si definujeme jednoduchý autoloader:

<?php

function autoloader($trida)
{
    if (!file_exists(__DIR__ . '/../../' . $trida . '.php'))
        return false;
    require(__DIR__ . '/../../' . $trida . '.php');
}

spl_autoload_register('autoloader');

Všimnite si, že autoloader vracia false v prípade, že sa mu nepodarí triedu načítať. To je veľmi dôležité, pretože to tak po ňom môžu prevziať ďalšie Autoloader Codeception, ktoré používa na svoje súbory.

Môže sa stať, že v predvolenom nastavení tieto súbory nebudú povolené. Skontrolujte preto radšej súbor codeception.yml, v zložke nadradenej zložke s testami, že obsahuje nasledujúce riadky:

settings:
    bootstrap: _bootstrap.php

Pre úplnosť prikladám, ako súbor vyzerá v mojom prípade:

paths:
    tests: tests
    output: tests/_output
    data: tests/_data
    support: tests/_support
    envs: tests/_envs
actor_suffix: Tester
settings:
    bootstrap: _bootstrap.php
extensions:
    enabled:
        - Codeception\Extension\RunFailed

Generovanie testov

Vygenerovanie nového unit testu prevedieme tiež príkazom v konzole:

test generate:test unit KalkulackaTest

Názov testu sa spravidla zostavuje ako názov testované triedy + slovo "Test", v našom prípade teda "KalkulackaTest". Meno testu si v príkaze vždy upravte podľa názvu triedy, kteoru testujete.

Ak vynecháte na konci názvu slovo "Test", bude automaticky doplnené. Možno teda použiť aj nasledujúci príkaz: test generate:test unit Kalkulacka a výsledok bude rovnaký.

Ak by ste niekedy potrebovali vygenerovať testy do inej zložky (napr. Mali viac zložiek tests v jednom projekte, jednu v priečinku app a druhú vo vendor), špecifikujete ju takto:

test --config=vendor/NejakyFrameworkSeSlozkouTests generate:test unit NejakyTest

Parametrom --config sa špecifikuje cesta k priečinku, ktorá obsahuje konfiguračný súbor codeception.yml, daná zložka teda musí obsahovať testy (štruktúru vygenerovanú pomocou bootstrap príkazu). Následne je možné takto generovať a spúšťať testy rôznych submodulov v systéme.

V priečinku unit sa nám vygeneroval nový súbor s nasledujúcim kódom:

<?php

class KalkulackaTest extends \Codeception\Test\Unit
{
    /**
     * @var \UnitTester
     */
    protected $tester;

    protected function _before()
    {
    }

    protected function _after()
    {
    }

    // tests
    public function testSomeFeature()
    {

    }
}

Prípadne môžete mať verziu PHPUnit, ktorá používa menné priestory, potom bude dediť z triedy pomenované len TestCase a nad ňou bude import z príslušného priestoru.

Asi vás neprekvapí, že je test triedy (scenár) reprezentovaný tiež triedou a jednotlivé testy metódami :) Čo je už zaujímavejšie je fakt, že na ňu nájdeme už predpripravené metódy. Tá posledná, začínajúce na slovo "test", bude ako každá metóda začínajúce na "test" automaticky spustená. Tie ďalšie 2 si vysvetlíme v nasledujúcej lekcii, Testovanie v PHP - Dokončenie unit testov .


 

Predchádzajúci článok
Úvod do testovania webových aplikácií v PHP
Všetky články v sekcii
Testovanie v PHP
Preskočiť článok
(neodporúčame)
Testovanie v PHP - Dokončenie unit testov
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity