Praktický návod
Jak psát PHPUnit testy, které mají skutečný smysl
Testuj pozorovatelné chování a důležitá rizika, ne pořadí privátních metod v implementaci.
Nejdřív stručně
Dobrý test je malá obchodní specifikace
Unit test má být rychlý a izolovaný od pomalých hranic, ale nemusí izolovat každou třídu od každé jiné třídy. Ověřuje výsledek nebo smluvenou komunikaci, která je součástí chování.
Integrační test použij tam, kde riziko vzniká skutečným SQL dotazem, mapováním ORM, serializací nebo konfigurací frameworku. Kombinace obou vrstev je užitečnější než vysoké procento coverage bez významných assertions.
Připrav si
Vyber chování, které stojí za ochranu
Začni drahými chybami a často měněnými pravidly, ne mechanickým testem každého getteru.
- PHPUnit nainstalovaný jako dev dependency a jednu standardní cestu pro spuštění celé suite.
- Popsaný vstup, pozorovatelný výstup a důležitá business invariant každého scénáře.
- Oddělené suites pro unit a integrační testy s předvídatelným prostředím.
- Možnost řídit čas, náhodu, UUID a externí hranice přes explicitní závislosti.
Kroky 1 až 3
Piš test od důvodu jeho existence
Název popíše pravidlo, tělo připraví minimum dat a assertion zkontroluje jeden souvislý výsledek.
1. Ověřuj chování ve tvaru arrange–act–assert
- Název testu formuluj jako scénář a očekávání, například paid_order_cannot_be_cancelled.
- Arrange připrav jen údaje důležité pro pravidlo, act zavolá veřejné API objektu a assert ověří výsledek, stav nebo doménovou výjimku.
- Test nesmí volat privátní metodu ani kopírovat produkční algoritmus. Po refaktoringu se stejným chováním má zůstat zelený.
- Pro hranice, ekvivalenční třídy a několik vstupů použij pojmenovaný data provider, aby byl při selhání vidět konkrétní případ.
vendor/bin/phpunit --testdox tests/Unit Oficiální PHPUnit dokumentace k psaní testů 2. Mockuj jen skutečné hranice
- Stub použij, když potřebuješ řídit nepřímý vstup, například odpověď platební brány. Mock použij jen tehdy, když samotná komunikace je požadovaný výstup.
- Nemockuj value object, entitu ani každou interní službu. Spleť očekávání typu exactly(1) často test zamkne k nepodstatné implementaci.
- Čas nahraď Clock rozhraním, náhodu generátorem a externí API malým vlastním portem. Test tak zůstane deterministický.
- U důležitého adaptéru doplň integrační nebo kontraktní test, protože mock neumí ověřit skutečný protokol a mapování.
$gateway = $this->createStub(PaymentGateway::class); Oficiální PHPUnit dokumentace k test doubles 3. Vrstvi testy podle rizika
- Čistá pravidla a value objekty ověř jednotkově. Repository testuj proti stejné databázi a constraintům jako v produkci.
- HTTP test pokryje důležitou cestu od requestu po response; několik end-to-end scénářů ověří kritický tok bez duplikace všech variant.
- Každý test vlastní data a po sobě zanechá známý stav. Nespoléhej na pořadí testů, lokální časovou zónu ani síť.
- Pomalé testy měř a opravuj. Paralelizuj až po odstranění sdílených souborů, portů a globální databázové identity.
vendor/bin/phpunit --testsuite=unit,integration Oficiální PHPUnit dokumentace k organizaci testů Krok 4
Ověř, že test opravdu chrání chybu
Zelený test může být bezcenný, pokud projde i po porušení pravidla.
-
Dočasně rozbij podmínku
Správný test selže s čitelným názvem a rozdílem. Potom produkční změnu vrať.
vendor/bin/phpunit --testdox -
Spusť suite vícekrát a v náhodném pořadí
Výsledek se nemění podle pořadí, času, lokálního prostředí ani zbytkových dat.
vendor/bin/phpunit --order-by=random -
Proveď bezpečný refaktoring
Změna vnitřní struktury bez změny veřejného chování nevyžaduje přepis poloviny mock očekávání.
Když to zlobí
Nejčastější chyby
Testy selhávají jen někdy
Najdi neřízený čas, random, pořadí, sdílenou databázi nebo síť. Závislost zpřístupni a nastav deterministicky.
vendor/bin/phpunit --order-by=random Každý refaktoring rozbije desítky testů
Testy ověřují interní volání. Přesuň assertions na veřejný výsledek a mockuj jen významné outbound hranice.
Coverage je vysoká, ale regresí přibývá
Coverage neříká, zda assertion chrání pravidlo. Doplň hraniční a chybové scénáře podle rizik.
Integrační suite je příliš pomalá
Profiluj ji, omez bootstrap a fixture, používej transakční izolaci a nejlevnější testovací vrstvu, která dané riziko zachytí.
Hotovo
Testy popisují pravidla, ne implementační šum.
Unit a integrační testy teď chrání správná rizika, používají test doubles střídmě a dávají čitelnou zpětnou vazbu při regresi.