Lekce 2 - Úvod do unit testů v PHP a instalace PHPUnit

PHP Testování Úvod do unit testů v PHP a instalace PHPUnit

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, Úvod do testování webových aplikací v PHP, jsme si udělali poměrně solidní úvod do problematiky. Také jsme si uvedli v-model, který znázorňuje vztah mezi jednotlivými výstupy fází návrhu a příslušnými testy.

Testy tedy píšeme vždy na základě návrhu, nikoli implementace. Jinými slovy, děláme je na základě očekávané funkčnosti. Ta může být buď přímo od zákazníka (a to v případě akceptačních testů) nebo již od programátora (architekta), kde specifikuje jak se má která metoda chovat. Dnes se budeme věnovat právě těmto testům, kterým říkáme jednotkové (unit testy) a které testují detailní specifikaci aplikace, tedy její třídy.

Pamatujte, že nikdy nepíšeme testy podle toho, jak je něco uvnitř naprogramované! Velmi jednoduše by to mohlo naše myšlení svést jen tím daným způsobem a zapomněli bychom na to, že metodě mohou přijít třeba i jiné vstupy, na které není vůbec připravená. Testování s implementací ve skutečnosti vůbec nesouvisí, vždy testujeme zda je splněno zadání.

Jaké třídy testujeme

Unit testy testují jednotlivé metody ve třídách. Pro jistotu zopakuji, že nemá valný smysl testovat jednoúčelové metody např. v modelech, které např. pouze něco vybírají z databáze. Abychom byli konkrétnější, nemá smysl testovat metodu jako je tato:

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

Metoda přidává položku do databáze. Typicky je použita jen v nějakém formuláři a pokud by nefungovala, zjistí to akceptační testy, jelikož by se nová položka neobjevila v seznamu. Podobných metod je v aplikaci hodně a zbytečně bychom ztráceli čas pokrýváním něčeho, co snadno pokryjeme v jiných testech.

Unit testy nalezneme nejčastěji u knihoven, tedy nástrojů, které programátor používá na více místech nebo dokonce ve více projektech a měly by být 100% funkční. Možná si vzpomenete, kdy jste použili nějakou knihovnu, staženou např. z GitHubu. Velmi pravděpodobně u ní byly také testy, které se nejčastěji vkládají do složky "tests", která je vedle složky "src" v adresářové struktuře projektu. Pokud např. píšeme aplikaci, ve které často potřebujeme nějaké matematické výpočty, např. faktoriály a další pravděpodobnostní funkce, je samozřejmostí vytvořit si na tyto výpočty knihovnu a je velmi dobrý nápad pokrýt takovou knihovnu testy.

Příklad

Jak asi tušíte, my si podobnou třídu vytvoříme a zkusíme si ji otestovat. Abychom se nezdržovali, vytvořme si pouze jednoduchou kalkulačku, která bude umět:

  • sčítat
  • odčítat
  • násobit
  • dělit

Vytvoření projektu

V praxi by ve třídě byly nějaké složitější výpočty, ale tím se zde zabývat nebudeme. Vytvořte si nový projekt s názvem kalkulacka a do něj si přidejte třídu Kalkulacka a následující implementací:

<?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 zajímavá pouze metoda vydel(), která vyvolá výjimku v případě, že dělíme nulou. Výchozí chování PHP je chyba skriptu, což by v aplikaci uživatel vidět nikdy neměl. Třída by mohla být klidně i ve jmenném prostoru, v testu by se poté naimportovala standardně pomocí use.

PHPUnit

V PHP se unit testy píší nejčastěji v PHPUnit frameworku, který by měl každý PHP programátor znát. Existují samozřejmě alternativní nástroje, např. Nette tester, které ale fungují všechny velmi podobně.

PHPUnit

I když bychom mohli PHPUnit nainstalovat samostatně, jak my v kurzu, tak vy u svých aplikací, budeme později potřebovat i další typy testů, alespoň ty akceptační. Na ty je třeba pracovat s dalšími nástroji a instalovat vše zvlášť by dalo poměrně dost práce. Proto PHPUnit nainstalujeme pomocí frameworku Codeception.

Codeception

Codeception je komplexní testovací framework pro PHP, který obsahuje:

  • PHPUnit
  • Akceptační wrapper pro Selenium
  • Další, pro nás nepodstatné testovací frameworky

Můžeme jej nainstalovat buď přes composer, nebo jednoduše stažením jediného souboru .phar. Pokud jste o .phar souborech ještě neslyšeli, jsou to spustitelné archivy s PHP aplikacemi, které lze spouštět např. přes vaše IDE.

Instalace

Já zde využiji první možnost přes .phar soubor, řešení přes composer je pro zájemce níže. Přejdeme na quickstart na adrese http://codeception.com/quickstart a stáhneme soubor codecept.phar, ideálně si jej uložte do složky s dnešním projektem.

Jelikož testy jsou již pokročilejší téma, použijeme pro něj i pokročilejší IDE - PhpStorm. Samozřejmě můžete testovat i v NetBeans, pokud z nějakého důvodu chcete.

Nyní vytvoříme pro .phar soubor alias, abychom ho mohli jednoduše spouštět z konzole. V aplikačním menu File zvolíme Settings a do vyhledávání nahoře napíšeme "Command Li", což nám otevře nástroj Command Line Tool Support.

Command Line Tools Support v PhpStorm

Pomocí tlačítka "+" vpravo nahoře přidáme novou položku typu "Custom tool" s viditelností pro projekt. Následně do formuláře vyplníme:

  • Tool path: C:\xampp\php\php.exe codecept.phar, cestu ke svému PHP interpreteru si případně upravte. Na Linuxu stačí zadat místo cesty jen php.
  • Alias: test (to je název příkazu, přes který budeme archiv spouštět)
Přidání Command Line Tool v PhpStorm

Všechna okna potvrďte.

Instalace přes Composer

Tato pasáž popisuje instalaci Codeception přes Composer. Pokud jej nepoužíváte, přeskočte ji. Máte-li Composer, Codeception nainstalujete příkazem composer require "codeception/codeception" --dev

Doporučuji si PhpStorm s Composerem a Codeception propojit, jinak pravděpodobně nebude fungovat napovídání kódu.

IDE by se mělo nastavit automaticky, nicméně raději přikládám screenshoty mého nastavení:

Nastavení Composeru v PhpStorm

Nastavení Composer autoloaderu v PhpStorm

Nastavení Codeception v PhpStorm

V tomto případě bude nastavená cesta pro příkaz Composeru jako cesta k batch souboru vygenerovaném přes Composer. Měla by vypadat takto: cesta_k_projektu\Kalkulacka\vendor\bin\codecept.bat. V případě Linuxu použijete soubor bez koncovky .bat.

Bootstrap

Nyní otevřeme menu Tools -> Run command a do konzole (pozor, neplést s terminálem, ten je v PhpStorm také) vložíme následující kód:

test bootstrap

Tím docílíme vygenerování testů do našeho projektu.

Všimněte si, že se v něm objevila složka tests, která obsahuje několik dalších souborů a podsložek. Pro nás bude zatím důležitá podsložka unit, do které budeme generovat nové unit testy. Jelikož testy používají třídy z naší aplikace, potřebují definovat minimálně autoloader. To se dělá v souborech _bootstrap.php, které jsou zde buď zvlášť pro každý typ testů nebo pro všechny testy.

My si v našem případě vytvoříme soubor _bootstrap.php ve složce 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šimněte si, že autoloader vrací false v případě, že se mu nepodaří třídu načíst. To je velmi důležité, jelikož to tak po něm mohou převzít další autoloadery Codeception, které používá na své soubory.

Může se stát, že ve výchozím nastavení tyto soubory nebudou povoleny. Zkontrolujte proto raději soubor codeception.yml, ve složce nadřazené složce s testy, že obsahuje následující řádky:

settings:
    bootstrap: _bootstrap.php

Pro úplnost přikládám, jak soubor vypadá v mém případě:

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

Generování testů

Vygenerování nového unit testu provedeme také příkazem v konzoli:

test generate:test unit KalkulackaTest

Název testu se zpravidla sestavuje jako název testované třídy + slovo "Test", v našem případě tedy "KalkulackaTest". Jméno testu si v příkazu vždy upravte podle názvu třídy, kteoru testujete.

Pokud vynecháte na konci názvu slovo "Test", bude automaticky doplněno. Lze tedy použít i následující příkaz: test generate:test unit Kalkulacka a výsledek bude stejný.

Pokud byste někdy potřebovali vygenerovat testy do jiné složky (např. měli více složek tests v jednom projektu, jednu ve složce app a druhou ve vendor), specifikujete ji takto:

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

Parametrem --config se specifikuje cesta ke složce, která obsahuje konfigurační soubor codeception.yml, daná složka tedy musí obsahovat testy (strukturu vygenerovanou pomocí bootstrap příkazu). Následně lze takto generovat a spouštět testy různých submodulů v systému.

Ve složce unit se nám vygeneroval nový soubor s následujícím kódem:

<?php

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

    protected function _before()
    {
    }

    protected function _after()
    {
    }

    // tests
    public function testSomeFeature()
    {

    }
}

Případně můžete mít verzi PHPUnit, která používá jmenné prostory, potom bude dědit z třídy pojmenované jen TestCase a nad ní bude import z příslušného prostoru.

Asi vás nepřekvapí, že je test třídy (scénář) reprezentovaný také třídou a jednotlivé testy metodami :) Co je již zajímavější je fakt, že na ni nalezneme již předpřipravené metody. Ta poslední, začínající na slovo "test", bude jako každá metoda začínající na "test" automaticky spuštěna. Ty další 2 si vysvětlíme v příští lekci, Testování v PHP - Dokončení unit testů.


 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
5 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 sítě 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
Aktivity (5)

 

 

Komentáře
Zobrazit starší komentáře (6)

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:10. července 18:18

Jestli se chceš od pokročilejších programátorů něco naučit, tak používej i jejich nástroje. NetBeans nejsou dobré IDE a používáme je jen v základních kurzech, aby člověk mohl jednoduše začít. Testování v nich bych nikdy nedělal a ani to tu nebudu nikoho učit, protože by to pro něj byla ztráta času, když se může učit s něčím kvalitním a stále dostupným.

Odpovědět 10. července 18:18
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
Pavel
Redaktor
Avatar
Odpovídá na David Čápka
Pavel:10. července 18:46

Aha.. takže když ty používáš PHPStorm tak je zaručeně nejlepší a ostatní nestojí za nic. Trošku subjektivní, nemyslíš?
Při vyhledání nej IDE je NetBeans na 2 místě (zdroj Příklady IDE ). Je to za rok 2018.
Já mám oba.. něco dělám v Storm, ale něco v NetBeasn. Je mi prostě bližší.

A pokud tvá rada znamená, kup si za 2,399Kč na rok PHPStorm, aby si byl dobrý programátor, tak si myslím, že není důležité v čem člověk vytváří, ale co vytváří a jak vytváří. (Mimochodem je zde i zmínka o editorech, ale to budou asi ty úplní amatéři, tak to nemá cenu ani zmiňovat :-) ).

To je vše co jsem ti chcel říct. Asi nemá cenu v této diskuzi pokračovat.

Ještě jednou zopakuji a prosím: pokuste se psát i pro začínající. Děkuji.

 
Odpovědět  -1 10. července 18:46
Avatar
Odpovídá na Pavel
Michal Šmahel:10. července 19:00

Nechci se zastávat ani jednoho z vás, nicméně bych ti rád něco předal. Jelikož již nečteš materiály čistě pro začátečníky (ale spíše lehce pokročilé), je třeba počítat s určitými změnami. Co se týče Netbeans, Eclipse apod., nejsou to špatná IDE, ale spíše nedostačující pro práci na "vyšší" úrovni. Proto se tu používá zrovna PHPStorm, který se řadí mezi pokročilé IDE a dokáže obsáhnout vše potřebné. Nikdo však neříká, abys za něho platil, v rámci studia, open-source a start-up projektů je zdarma. Také tě David nenutí ho používat. Jsou i jiné IDE, můžeš používat ty. Co se týče editorů, mnohdy na tom nejsou úplně špatně, ale málokdy stačí na složitější věci. Pravdou však je, že čím pokročilejší věci děláš, tím víc to může stát (potřebuješ i pokročilejší nástroje), ale také nemusí, když si to umíš dobře zařídit.

Odpovědět 10. července 19:00
Nejdůležitější je motivace, ovšem musí být doprovázena činy.
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Pavel
David Čápka:10. července 19:50

tak si myslím, že není důležité v čem člověk vytváří, ale co vytváří

Tak to piš v notepadu :) Jestli je pro tebe 2399 Kč moc na to, aby jsi se naučil něco pro profesi, kde je plat 50.000 Kč/měsíc (PHP senior standardní plat), tedy ročně 600.000 Kč, tak to snad raději ani nezkoušej, protože to vyžaduje určité ambice a určitý typ myšlení. Chtěl jsem ti pomoct, nejde to, dále to už neřešme.

Odpovědět  ±0 10. července 19:50
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
Lien
Člen
Avatar
Odpovídá na David Čápka
Lien:18. července 14:05

Asi budu za idiota, zkusil jsem nainstalovat PHPStorm (evaulate verzi, abych si práci v něm vyzkoušel) a zjistil jsem, že nejsem schopen (při prvním spuštění, základním nastavení) v editoru napsat ">"
Což je u PHP/HTML editoru celkem podstatná závada a na první pokus mě to vcelku odradilo...
Používám české rozložení klávesnice...

 
Odpovědět 18. července 14:05
Avatar
Odpovídá na Lien
Andy Scheuchzer:18. července 14:22

"<" je pravý alt + ,
">" je pravý alt + .

Editováno 18. července 14:22
Odpovědět 18. července 14:22
Od ASM úroveň jazyků pouze klesá…
Avatar
Odpovídá na Andy Scheuchzer
Andy Scheuchzer:18. července 14:27

Teď jak to čtu znova si říkám že jsem asi špatně pochopil dotaz.

Odpovědět  +1 18. července 14:27
Od ASM úroveň jazyků pouze klesá…
Avatar
Odpovídá na Lien
Štěpán Halíř:18. července 16:17

Ahoj,
koukni sem na komentář Davida Jančíka. Jeho návod mi pomohl. :)

Odpovědět  +2 18. července 16:17
Per aspera ad astras
Avatar
Lien
Člen
Avatar
Odpovídá na Štěpán Halíř
Lien:19. července 7:54

No paráda! Super, funguje. Díky!
teď jenom přijít na to, proč se mi to nesynchronizuje na vzdálený server přes SFTP %P

 
Odpovědět  +1 19. července 7:54
Avatar
rosatislav
Člen
Avatar
Odpovídá na Pavel
rosatislav:13. srpna 22:22

Zdravím. V tom že phpstorm stojí ročně něco přes 2 tisíce bych vůbec neviděl problém. Pokud se někdo programováním živí, tak věřím že si to dovolit může.

A pokud narážíš na to, že ne všichni si to dovolit můžou, tak většinou to mohou být studenti a Ti pokud vím mají licenci zdarma.

Navíc pokud by se David neustále musel ohlížet na to, že někdo používá netbeans a někdo phpstorm, tak by potom polovina každého článku byla zabraná tím, jak to udělat v onom IDE a to přece nemá být smyslem tohoto kurzu.

Psát pro začínající testování mi přijde úplně mimo mísu, protože ten kdo programuje delší dobu tak nemá problém s tím si vygooglit, jak se co udělá v IDE, které používá.

A lidé, kteří si čas od času něco zbastlí ve volném čase opravdu nebudou číst jak psát testy pro jejich aplikace.

 
Odpovědět 13. srpna 22:22
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.

Zobrazeno 10 zpráv z 16. Zobrazit vše