Proč používáme mock objekty?

Pozdržení volby infrastruktury

Řekněme, že vytváříme kód, který komunikuje s databázovým rozhraním. O implementaci tohoto rozhraní není zatím rozhodnuto, my však můžeme náš kód vesele vyvíjet dále. V našich jednotkových testech začneme využívat mock objekt, který bude napodobovat minimální sadu vlastností, již očekáváme od našeho databázového rozhraní. Tento objekt nám bude současně toto rozhraní také definovat. O jeho implementaci se prozatím nemusíme starat.

Napodobování stavu systému

Jednotkové testy, které záleží na komplexním stavu systému, může být těžké nastavit, speciálně v případě, že zbytek systému ještě není dokončen. Mock objekty řeší tento problém a jsou schopny poskytnout jednoduchou emulaci požadovaného stavu systému. Navíc, tento stav je zapouzdřen pouze do jednoho mock objektu a není rozptýlen po mnoha jednotkových testech, takže jej můžeme kdykoli znovu použít.

Napodobování složitých podmínek

Některé jednotkové testy potřebují testovat podmínky, které je velmi obtížné napodobovat, např. selhání serveru. Zde opět můžeme použít mock objekt, který implementuje lokální rozhraní (proxy) pro tento server. Každý jednotkový test poté může konfigurovat tuto proxy na selhání a programátoři se musí pokusit napsat klientský kód tak, aby testem prošel.

Zobrazení chyb

Při jednotkovém testování testujeme části kódů na výskyt chyb. Při stavovém jednotkovém testování cílového kódu probíhá kontrola předpokládaného stavu oproti skutečnému stavu na konci samotného testu. Toto je důvod, proč je složité určit přesné místo výskytu chyby. Při interakčním jednotkovém testování dochází k vyhodnocování správnosti předpokládaného tvrzení při každé interakci našeho mock objektu s testovaným kódem. Z toho důvodu je snazší určit přesné místo výskytu chyby a vygenerovat použitelnou hlášku, která navíc obsahuje popis rozdílů mezi očekávaným a aktuálním stavem hodnot.

Znovupoužitelná tvrzení

Při stavovém testování obsahuje každý jednotkový test sadu tvrzení týkajících se doménového kódu. Tato tvrzení mohou být umístěna do sdílených metod pro použití ve více jednotkových testech, nicméně programátor nesmí zapomenout je v nových testech aplikovat. Nadruhou stranu, tato tvrzení mohou být reprezentována mock objektem a budou aplikována v případě, že použijeme tento mock objekt v nějakém testu.

S tím, jak sada jednotkových testů roste, tento mock objekt můžeme využít napříč celou touto sadou a tato tvrzení aplikovat na nový kód. Podobně, pokud přibude nové tvrzení, stačí ho jednou přidat do našeho mock objektu a toto tvrzení bude automaticky aplikováno ve všech existujících testech, kde je tento objekt použit.

Jednou za čas se stane, že některé z existujících tvrzení již nadále není třeba. Tyto případy ukazují kandidáty na refaktorizaci jak doménového kódu, tak i kódu jednotkového testu.

Lepší styl a strukturovanost kódu

Bylo prokázáno, že využívání mock objektů má podíl na produkci kvalitnějšího kódu. Mock objekty jsou navrženy tak, aby podporovaly objektově orientovaný kód psaný stylem „Říkej, neptej se (Tell, Don't Ask). Při tomto stylu kódování dělají objekty rozhodnutí na základě dat, která interně obsahují nebo která získají skrze parametry vlastních metod. To ovšem neznamená, že objekty se nesmí tázat jiných objektů na další data. Časem se však ukázalo, že se potřebujeme ptát pouze v případech vyhledávání, řazení a filtrování objektů nad kolekcemi. Ostatní informace můžeme předávat jako parametry metod.

Pokud píšeme náš kód stylem Tell, Don't Ask, vyvíjíme náš kód podle Zákonu Demétera (Law of Demeter). Tento zákon říká, že objekty komunikují, volají metody, pouze svých nejbližších sousedů. Nikdy nekomunikují tak, že jeden objekt volá metodu jiného objektu skrze svého souseda. Výhodou tohoto stylu návrhu je, že objekty jsou vnitřně soudržné (koherentní), ale nejsou nazvájem příliš provázané a lze je tedy v případě potřeby zaměnit.

A co na to mock objekty? Rozhraní objektu nedefinuje metody, které poskytují informace o stavu dané implementace tohoto objektu, nýbrž definuje metody, které slouží jako příkazy prováděné tímto objektem. Jediným způsobem jak sledovat vlastnosti daného objektu je tedy pozorovat, jak se chová na základě příkazů zasílaných jiným objektům. Sledujeme, jak mění stav světa. Mock objekty nám právě umožňují sledovat a verifikovat tyto příkazy.

Extrémním protipříkladem stylu kódování Tell, Don't Ask je řetězení method, jak je zachyceno na následující ukázce.

SportEvent sportEvent = new SportEvent();
Competitor competitor = new Competitor();
sportEvent.getCompetitors().add(competitor);

Tento kód trpí velkým provázáním, je méně čitelný a je těžko testovatelný pomocí mock objektů. Tento kód však můžeme upravit do podoby vyhovující stylu "Tell, Don't Ask" následovně.

...
sportEvent.addCompetitor(competitor);

Tento kód již není implementačně závislý na kolekci soutěžících. Nyní může být tato kolekce implementována libovolným způsobem, aniž by to mělo nějaký dopad na zbývající část zdrojového kódu aplikace. Čitelnost tohoto kódu také není zanedbatelným přínosem.

Objevování rozhraní

Mockování objektů je výbornou technikou pro definování rozhraní nahrazovaných objektů. Pro otestování každé nové funkcionality napíšeme jednotkový test využívající mock objekty pro simulaci chování spolupracujících objektů. Každý mock objekt je představitelem toho, co má reálný kód opravdu umět. Jakmile máme stabilizovaný doménový kód i mock objekty, napodobující vlastnosti spolupracujících objektů, můžeme z těchto mock objektů vyextrahovat jejich interakce s doménovým kódem a tím definovat nová rozhraní, která musí tyto spolupracující objekty implementovat.

Komentáře

Téma neobsahuje žádné komentáře.

Vložit komentář

Můžete používat značkovací jazyk Texy!


Jméno:
E-mail:
Url:
Komentář:
1 + 2 =
 
MoroSystems, s.r.o.