Jak mockovat final třídy?
Jak mockovat třídy, které jsou definované jako final nebo některé z jejich metod jsou final?
Mockování znamená nahrazení původního objektu za jeho testovací imitaci, která neprovádí žádnou funkci a jen se tváří jako původní objekt. A předstírá chování, které potřebujeme kvůli testování.
Takže například místo objektu PDO s metodami jako query() apod. vytvoříme jeho mock, který práci s databází jen předstírá, a místo toho ověřuje, že se volají ty správné SQL příkazy atd. Více třeba v dokumentaci Mockery.
A aby bylo možné mock předávat metodám, které mají type hint
PDO
, je potřeba, aby i třída mocku dědila od PDO. A to může
být kámen úrazu. Pokud by totiž třída PDO nebo metoda query() byla final,
už by to nebylo možné.
Existuje nějaké řešení? První možnost je final
vůbec
nepoužívat. To ovšem nepomůže s kódem třetích stran, který final
používá, ale hlavně se tím ochuzujeme o důležitý prvek objektového
návrhu. Existuje dogma, že každá třída by měla být buď final, nebo
abstract.
Druhou a velmi šikovnou možností je použít Nette Tester, který od verze 2.0 disponuje
vychytávkou, která odstraňuje z kódu klíčové slovo final
on-the-fly. Stačí na začátku testu zavolat:
require __DIR__ . '/vendor/autoload.php';
Tester\Environment::bypassFinals();
A je to. Je za tím ukrytá neskutečně černá magie 🙂
Pokud nepoužíváte Nette Tester, ale třeba PHPUnit, nebudete ochuzeni, stačí si nainstalovat BypassFinals:
composer require dg/bypass-finals --dev
A na začátku skriptu zavoláte:
require __DIR__ . '/vendor/autoload.php';
DG\BypassFinals::enable();
Komentáře
SendiMyrkr #1
Není tam pak problém, že když z ne-final metody udělám final metodu, tak se při testech jejích potomků nedozvím, že je tam chyba?
Svaťa Šimara #2
Toto řešení mi přijde dost ujeté. Zkusím paralelu s testováním private metod – jde to zařídit, ale pokud testuju privátní metody, dělám něco špatně. Stejně tak pokud mockuju final třídu, dělám něco špatně.
Kód třetí strany do svého zaintegruji pomocí vlastního adaptéru
taco #3
#2 Svaťa Šimara, To přirovnání mi nesedí. Privátní metoda deiure neexistuje, proto ji nemohu testovat. Final třída je final proto, že autor architektury zavrhuje vytvářet specializované verze té třídy. Jenže mockování třídy asi nebude to o co autorovi šlo. (Viz v článku odkazované dogma.)
Svaťa Šimara #4
#3 taco, Ovlivňuje testování návrh kódu? Pokud budeme nejdříve psát testy a až poté implementaci, pak ano. V testech se nejdříve rozhodnu něco mocknout, pak jdu do implementace, kde mockování zakážu. No, a pak v duchu „protože to jde“ začnu hackovat problémy, které jsem si právě zařídil.
Dogmata… Výše zmiňované dogma zase za pár let opustíme, protože se neukáže praktické.
Ale abych se vrátil k myšlence proč mi to přijde ujeté. Mockujme pouze rozhraní a máme po problémech. Tím ale nemyslím mít proboha na všechno rozhraní. Prostě v testech tam, kde to bude potřeba, použiju daný objekt, jinde mock rozhraní.
v6ak #5
Bude to fungovat, když všechny výskyty slova “final” budou psány s alespoň jedním písmenem velkým? Co jsem projel zdroják, tak IMHO ne, strpos selže a soubor to nijak nemodifikuje.
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.