Na navigaci | Klávesové zkratky

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

  1. SendiMyrkr #1

    avatar

    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?

    před 7 lety
  2. Svaťa Šimara #2

    avatar

    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ě.

    …nepomůže s kódem třetích stran…

    Kód třetí strany do svého zaintegruji pomocí vlastního adaptéru

    před 7 lety | reagoval [3] taco
  3. taco #3

    avatar

    #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.)

    před 7 lety | reagoval [4] Svaťa Šimara
  4. Svaťa Šimara #4

    avatar

    #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í.

    před 7 lety
  5. 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.

    před 7 lety

Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.


phpFashion © 2004, 2024 David Grudl | o blogu

Ukázky zdrojových kódů smíte používat s uvedením autora a URL tohoto webu bez dalších omezení.