Slovník pojmů
Unit test
Unit test dává rychlou zpětnou vazbu o pravidle v kódu. Nemá dokazovat, že spolu fungují databáze, HTTP server a prohlížeč.
Stručná definice
Malý test jednoho chování s plně řízenými vstupy.
Unit test spustí jednu třídu či malou spolupracující část a ověří výstup, stav nebo podstatný vedlejší efekt. Skutečnou databázi, síť a čas obvykle nahradí řízenou implementací, aby výsledek nezávisel na pořadí testů ani na dostupnosti služby.
Unit test není PHPUnit: PHPUnit je konkrétní framework. Není to ani integrační test ani E2E test. Integrační test ověřuje spolupráci skutečných částí; E2E test prochází aplikaci z pohledu uživatele, často přes Playwright.
Jaký problém řeší
Rychlou kontrolu pravidel bez provozního okolí
Dobře zvolený unit test zachytí chybu blízko změny a ukáže, které pravidlo přestalo platit.
- výpočet ceny, daně nebo slevy z přesně zadaných hodnot
- validace vstupu a převod na hodnotový objekt
- rozhodnutí workflow podle stavu objednávky
- převod odpovědi externího API na vlastní datový model
- zpracování chyby závislosti bez skutečného volání služby
Praktický příklad
Sleva objednávky bez databáze a bez času systému
Test ověřuje pravidlo, ne ORM ani HTTP. Hodnoty jsou v testu přímo vidět, proto je scénář deterministický: stejný vstup má vždy stejný výsledek.
Pokud sleva závisí na aktuálním čase nebo vzdáleném ceníku, služba má dostat čas či ceník jako závislost. Test pak předá pevnou hodnotu nebo stub.
PHP
final class DiscountCalculatorTest extends TestCase
{
public function testVipCustomerGetsTenPercentDiscount(): void
{
$calculator = new DiscountCalculator();
self::assertSame(9000, $calculator->priceInCents(10_000, true));
}
}
Diagram rozsahu
Unit test a integrační test ověřují jinou hranici
Diagram: unit test = pravidlo + řízené doubles; integrační test = skutečné části + testovací infrastruktura.
- Unit: připrav vstup Vytvoř testovaný objekt a malé hodnoty v paměti. Závislosti řiď stubem, mockem nebo fake implementací.
- Unit: proveď a ověř Zavolej jedno chování a ověř očekávaný výsledek. Test nemá čekat na síť ani sdílet stav.
- Integrace: připrav infrastrukturu Použij skutečný databázový adapter, Redis, HTTP adapter nebo kontejner v izolovaném testovacím prostředí.
- Integrace: ověř spolupráci Zkontroluj hranici, například SQL mapování či serializaci HTTP. Data po testu vyčisti nebo vrať transakci.
- E2E: ověř uživatelský tok Projde aplikaci jako uživatel přes prohlížeč; je širší a pomalejší než oba předchozí typy.
Nástroje a hranice
Framework test spustí, návrh rozhoduje, co je jednotka.
Nástroj pomáhá psát assertion a doubles, ale sám neurčí správnou míru izolace.
PHPUnit
Framework pro test runner, assertions, fixtures, stubs a mocky. Ne každý PHPUnit test je unit test.
Playwright
Browserový E2E nástroj; ověřuje širší hranici než unit test.
CI/CD
Rychlé unit testy mohou běžet nad každou změnou. Integrace a E2E se řadí podle rizika a času.
Důležité pojmy
Izolace není totéž co testovat interní detaily.
Cílem je řídit okolí testovaného chování a ponechat test čitelný při rozumném refactoringu.
Izolace a determinismus
Test nemá záviset na pořadí, náhodě, aktuálním čase, sdílené databázi ani dostupnosti externí služby.
Stub
Nahrazuje závislost a vrací předem určená data nebo chybu. Použij ho, když test řídí nepřímý vstup.
Mock
Ověřuje podstatnou komunikaci se závislostí. Neověřuj jím každé interní volání, jinak test svážeš s implementací.
Fake
Jednoduchá funkční náhrada, například paměťový repozitář. Není to skutečná databáze.
Fixture
Vstupní stav testu. Má být malý a u testu čitelný; velké sdílené fixture zakryjí scénář.
Výhody a omezení
Rychlý test pravidla není důkaz celé integrace.
Přínosy
- rychlé a levné spouštění i ve velkém počtu
- chyba ukazuje na malé pravidlo místo celého systému
- snadné pokrytí hraničních hodnot a chyb závislosti
- testy mohou běžet paralelně bez sdíleného stavu
Na co pozor
- mock nevystihne chybný SQL dotaz, síťovou konfiguraci ani skutečnou serializaci
- příliš detailní mocky lámou test při neškodném refactoringu
- test závislý na hodinách, náhodě či globálním stavu bývá flaky
- unit test nenahrazuje integrační ani browserový E2E scénář
Kdy dává smysl
Pro důležitá pravidla, která mají být rychle pochopitelná a levně ověřitelná.
Unit test se hodí pro ceny, stavy objednávek, validaci, mapování a rozhodování aplikace. Čím lépe jsou závislosti předané přes jasný kontrakt, tím snáz je lze řídit bez připojení k provozním službám.
Nezkoušej unit testem dokázat, že ORM uloží data, že Redis funguje nebo že API adapter rozumí skutečné odpovědi. To jsou vhodné případy pro integrační test nad testovací infrastrukturou.
Na co myslet
Krátký test má zůstat důvěryhodný i při paralelním běhu.
Testy popisují kontrakt chování, ne náhodný postup vnitřní implementace.
- držet jeden srozumitelný scénář a očekávaný výsledek na test
- čas, náhodu, identifikátory a externí vstupy předávat tak, aby šly určit
- preferovat stub pro hodnotu a mock jen pro skutečně důležitou komunikaci
- nepoužívat sdílené mutable fixture ani závislost na pořadí testů
- spouštět unit testy rychle v CI/CD společně se statickou analýzou
- při změně adapteru doplnit integrační test místo rozšiřování mocku do simulace infrastruktury
Časté otázky
Unit test bez záměn
Je každý PHPUnit test unit test?
Ne. PHPUnit je framework. Test s připojenou databází nebo HTTP serverem může být integrační test.
Je mock vždy lepší než skutečná závislost?
Ne. Stub se hodí pro řízený vstup, mock pro ověření důležité komunikace uvnitř unit testu. Skutečný adapter má být ověřen integračním testem.
Co je fake?
Jednoduchá funkční náhrada závislosti, například paměťový repozitář. Má stejný kontrakt, ale ne stejné provozní vlastnosti jako databáze.
Nahradí unit test E2E test?
Ne. Unit test rychle kontroluje pravidlo, E2E test ověřuje vybraný tok přes rozhraní aplikace.
Jak držím kvalitu vývoje
Testy rozděluji podle rizika a hranice, kterou mají opravdu ověřit.
U PHP aplikací kombinuji rychlé unit testy s cílenou integrací a automatizovanými quality gates, aby zpětná vazba byla rychlá i důvěryhodná.