Mock testing - Potěmkinovy vesnice
Řada z vás možná už na výraz Mock testing narazila, nÄ›kteřà ne. Pro ty z vás, kteřà Mock pÅ™Ãstup v testovánà nepoužili je tento Älánek. Pro ostatnà může být zajÃmavá ukázka této techniky na knihovnÄ› EasyMock.
Jedná se vlastnÄ› o techniku psanà urÄitého druhu automatických testů. V podstatÄ› se jedná o nahrazenà reálného objektu testovacà fasádou, která neprovádà žádnou funkcionalitu nahrazovaného objektu - jen se jako tento objekt tvářÃ. MÃsto původnà logiky objektu je vloženo chovánÃ, které ve svém testu potÅ™ebujete.
Nejlepšà bude teorii provázat s praxÃ. Pokud pÃÅ¡ete automatické testy, jistÄ› jste narazili na situaci, kdy k napsánà jednoduchého testu musÃte velmi složitÄ› a pracnÄ› pÅ™ipravovat okolnà podmÃnky. NapÅ™. testujete metodu v business objektu, který se dotazuje internÄ› DAO objektů na data v databázi. To ale znamená, že pÅ™ed tÃm, než zaÄnete testovat logiku tohoto business objektu, musÃte pÅ™ipravit správná data v databázi. MusÃte také zajistit, že se tam omylem nedostanou dalšà data, která by test zhatila (napÅ™. v SELECT dotazu vrátila vÃce řádků) a tudÞ obvykle zase musÃte po sobÄ› uklÃzet. OÅ¡etÅ™enà okolnÃch podmÃnek spuÅ¡tÄ›nà testu vás nakonec může stát nÄ›kolkrát vÃce Äasu, než potÅ™ebujete k napsanà vlastnà testovacà logiky.
Existujà různé techniky, jak toto zajistit - setkal jsem se napÅ™. s použitÃm HSQL databáze, která byla celá v pamÄ›ti a po každém testu se kompletnÄ› dropla a pÅ™ed novým opÄ›t vytvoÅ™ila a dosadila se pÅ™ÃsluÅ¡ná testovacà data (tÃm, že se vÅ¡e odehrávalo pouze v pamÄ›ti byly testy velmi rychlé). OvÅ¡em lehÄà je dle mého názoru právÄ› použÃt techniku mock objektů.
PÅ™i použità této techniky se soustÅ™edÃte na testovánà pouze logiky business objektu a pÅ™edpokládáte že okolnà objekty fungujà správnÄ› tak jak majÃ. Potom v naÅ¡em pÅ™ÃkladÄ› za DAO objekty dosadÃte pouze “prázdné” ulity, které majà stejné rozhranÃ, ale mÃsto vlastnà logiky obsahujà logiku takovou, kterou potÅ™ebujete pro vlastnà test. Tzn. ve chvÃli, kdy se business objekt zeptá DAO na konkrétnà data, neprobÄ›hne dotaz do databáze, ale vy mu rovnou vrátÃte data, která v daném testu potÅ™ebujete.
To je celý princip mock objektů. Nic vÃc, nic mÃň. Pokud se chcete dozvÄ›dÄ›t nÄ›co vÃc, pokraÄujte ve ÄtenÃ.
Pokud chcete použÃt tuto techniku pÅ™i testovánÃ, potÅ™ebujete vyÅ™eÅ¡it dva problémy:
V podstatÄ› to znamená, abyste mohli nahradit původnà reálný objekt náhražkou napÅ™. pÅ™es setter metodu, konstruktor nebo jako parametr testované metody. Velmi prosté, ale v nÄ›kterých pÅ™Ãpadech, kdy nemáte pod kontrolou API testovaného objektu, to vyluÄuje samotné použità této techniky. možnost nahradit originálnà logiku nahrazovaného objektů testovacà logikou - princip OOP
Tzn. testovaný objekt nesmà poznat, že pracuje s náhražkou. Musà být zachováno rozhranà původnÃho objektu a objekty musà být typovÄ› stejné.
To se dá v JavÄ› udÄ›lat jen dvÄ›ma způsoby: dÄ›dÄ›nÃm nebo implementacà interface. Vytvářet mocky dÄ›dÄ›nÃm bych nedoporuÄoval, jelikož potom máte v mock objektu fragmenty původnà logiky, což vám to může mnohokrát akorát celé zamotat. Ideálnà je tedy nadefinovat rozhranÃ, se kterým testovaný objekt pracuje a dané rozhranà je implementováno dvÄ›mi tÅ™Ãdami - tou reálnou a faleÅ¡nou “mock” implementacÃ. Je to trochu pracnÄ›jšà způsob, ale ÄistÅ¡Ã.
Ve vlastnÃm testu potom jednoduÅ¡e vytvoÅ™Ãte testovaný objekt, nastavÃte mu mÃsto původnà reálné implementace testovacà náhražku a zajistÃte, aby ta vracela data, která ve svém testu požadujete.
Můžete se vydat cestou tzv. stub objektů - což znamená že vytvoÅ™Ãte fyzicky tÅ™Ãdy, které obsahujà logiku, kterou od nich požadujete v testech. Je to asi jednoduššà cesta pro nÄ›koho, kdo s tÃmto způsobem testů jeÅ¡tÄ› nepÅ™iÅ¡el do styku, ale cesta dosti pracná.
DalÅ¡Ãm vývojovým stupnÄ›m jsou tzv. mock objekty. Jejich použità je možná obtÞnÄ›jÅ¡Ã, zato pÅ™inášejà jednu zásadnà výhodu. Dynamické mock objekty jsou vytvářeny pÅ™Ãmo testem a existujà pouze virtuálnÄ›. Tzn. nenà tÅ™eba fyzicky implementovat vlastnà mock tÅ™Ãdu a navÃc vám zdarma poskytnou Å™adu funkcionality, kterou byste do svých stub objektů pracnÄ› implementovali. PÅ™Ãklad použità mock objektů si ukážeme na použità knihovny EasyMock.
Dejme tomu, že máme tento business objekt, který chceme testovat.
Ten použÃvá ke své Äinnosti dvÄ› rozhranà - IMessageSender a ILogger. Vlastnà logika business objektu je velmi prostá, pokud dojde ÄÃslo menšà jak 10, poÅ¡le email pÅ™Ãjemci jedna. Pokud je ÄÃslo vÄ›tÅ¡Ã, poÅ¡le mail pÅ™Ãjemci dva. Pokud se nepodařà odeslat email, tuto chybu zaloguje. Uvedená dvÄ› rozhranà jsou zobrazena nÞe.
Výše uvedenou logiku bychom normálnÄ› obtÞnÄ› testovali, ale s použitÃm mock objektů to bude triviálnÄ› jednoduché. Kompletnà zdrojové kódy jako Maven 2 projekt naleznete na konci Älánku. V nÄ›m je implementace testů jak pomocà Mock, tak i Stub objektů. Nuže, jak vypadá testovánà s knihovnou EasyMock?
Test prvnÃ:
V prvnÃm testu chceme otestovat, že pokud je vstupnà hodnota 1 odeÅ¡le se email prvnÃmu pÅ™Ãjemci a zároveň se nezaloguje žádná chyba (odeslánà se podaÅ™Ã).
Ve výše uvedeném kódu je nÄ›kolik zajÃmavých mÃst.
#1 - zde se vytvářà virtuálnà mock objekt implementujÃcà specifikované rozhranÃ; fakticky zde v tuto chvÃli knihovna EasyMock vytvořà novou tÅ™Ãdu implementujÃcà vaÅ¡e rozhranà a instanciuje objekt této virtuálnà tÅ™Ãdy;
#2 - zde zavoláme metodu naÅ¡eho rozhranà stejnÄ›, jako bychom to udÄ›lali s reálným objektem; v tomto pÅ™ÃpadÄ›, vÅ¡ak pouze instruujeme mock objekt, že má oÄekávat (po odstartovánà skuteÄného testu - viz. bod #3) volánà metody send s uvedenými parametry
#3 - zde Å™ekneme knihovnÄ› EasyMock: až do tohoto bodu jsme programovali tvé chovánà pro daný test; od této chvÃle se chovej dle naÅ¡ich instrukcà (viz. bod #2)
#4 - nastavÃme mock objekty do testovaného objektu - tzn. mÃsto původnÃch reálných implementacà mu podstrÄÃme kukaÄÄà vejce v podobÄ› naÅ¡ich mock objektů
#5 - zavoláme testovanou metodu business objektu - ta probÄ›hne standardnÃm způsobem a business objekt pÅ™i svém chodu zavolá metodu rozhranà IMessageSender na naÅ¡em mock objektu - volánà probÄ›hne bez problémů, tudÞ si business objekt myslÃ, že vÅ¡e probÄ›hlo v pořádku (náš mock objekt se tvářil jako reálný objekt)
#6 - verifikace mock objektů - toto je hlavnà pasáž mock testovánÃ; zde EasyMock prověřÃ, zda celá akce probÄ›hla tak, jak jste ho pÅ™ed replay fázà nauÄili; tzn. ověřÃ, že mezi replay a verify doÅ¡lo k jednomu volánà metody send na messageMock objektu a že vstupnà parametry odpovÃdaly (equals) vámi definovaným hodnotám z bodu #2; pokud by k volánà nedoÅ¡lo, nebo doÅ¡lo vÃcekrát, Äi nÄ›která z hodnot se liÅ¡ila od vámi specifikovaných, dojde zde k vyjÃmce a celý test selže; taktéž si povÅ¡imnÄ›te že souÄástà verify je i loggerMock objekt, u kterého jsme neprovádÄ›li žádnou “instruktáž” - v takovém pÅ™ÃpadÄ› ve fázi verify ověřà EasyMock knihovna, že nebyla zavolána žádná metoda tohoto rozhranà (což je dobÅ™e, protože v takovém pÅ™ÃpadÄ› by se business objekt zachoval Å¡patnÄ›)
Test druhý:
V druhém testu chceme simulovat chybu odeslánà zprávy. Objekt implementujÃcà IMessageSender rozhranà by v metodÄ› send mÄ›l vyvolat chybu SendException. Business objekt by na to mÄ›l reagovat zalogovánÃm chyby rozhranÃm ILogger.
#1 - vytvoÅ™Ãme business objekt
#2 - opÄ›t vytvoÅ™Ãme virtuálnà mock objekty
#3 - nynà instruujeme EasyMock, že má oÄekávat volánà metody logError na objektu loggerMock - pÅ™iÄemž prvnà parametr musà být ne null objekt (String) a druhým parametrem má být objekt pÅ™etypovatelný na tÅ™Ãdu SendException (v této Äásti vidÃte, že argumenty nemusÃte uvádÄ›t exaktnÄ› pÅ™esnÄ› - EasyMock poskytuje abstraktnÄ›jšà výrazové prostÅ™edky)
#4 - zde instruujeme EasyMock, že má oÄekávat volánà metody send na objektu messageMock s konkrétnÃmi vstupnÃmi hodnotami (obdobnÄ› jako v testu 1)
#5 - zde je dalšà novinka, zde Å™Ãkáme EasyMocku aby v poslednÃm volánà (viz. bod #4) vyhodil jako výsledek volánà exception SendException (obdobnÄ› bychom mohli EasyMocku Å™Ãct, aby poslednà volánà vrátilo konkrétnà návratovou hodnotu zámÄ›nou andThrow volánÃm metody andReturn)
#6 - nynà řekneme knihovnÄ› EasyMock: až do tohoto bodu jsme programovali tvé chovánà pro daný test; od této chvÃle se chovej dle naÅ¡ich instrukcÃ
#7 - nastavÃme mock objekty do testovaného objektu - tzn. mÃsto původnÃch reálných implementacà mu podstrÄÃme kukaÄÄà vejce v podobÄ› naÅ¡ich mock objektů
#8 - zavoláme testovanou metodu business objektu - ta probÄ›hne standardnÃm způsobem a business objekt pÅ™i svém chodu zavolá metodu rozhranà IMessageSender na naÅ¡em mock objektu - pÅ™i volánà metody send náš mock vyhodà SendException a náš business objekt by se mÄ›l patÅ™iÄnÄ› zareagovat zalogovánÃm chyby
#9 - verifikace mock objektů - EasyMock ověřÃ, zda na messageMock objektu byla zavolána právÄ› jednou metoda send a parametry volánà odpovÃdaly (equals) pÅ™edpisu z kroku #4; dále ověřÃ, že na objektu loggerMock byla právÄ› jednou zavolána metoda logError s prvnÃm ne nullovým parametrem a druhým parametrem pÅ™etypovatelným na SendException
Výše uvedené pÅ™Ãklady prezentujà pouze základnà a jednoduché možnosti knihovny EasyMock. V druhé verzi má již pomÄ›rnÄ› rozsáhlé možnosti “imitace” a ověřovánà požadovaného chovánÃ. NapÅ™Ãklad umožňuje parametry porovnávat ne jen na ekvalitu, ale má Å™adu výrazových prostÅ™edků z nichž jsme uvedli pouze dva (napÅ™. je / nenà null, test na regulárnà výraz, aritmetické operátory >,<,= nebo spojovánà podmÃnek pÅ™es logické operátory and, or). PoÄet volánà metod lze definovat pÅ™esnÄ› (napÅ™. právÄ› jedno jako v naÅ¡ich testech) nebo v urÄitém limitu - alespoň jednou, od - do, libovolnÄ›. Umožňuje ověřovat poÅ™adà volánà konkrétnÃch metod a naopak. Dokáže vytvoÅ™it mock objekty v různých režimech - strict mock (oÄekává volánà jen vámi pÅ™edepsaných metod - jinak generuje UnexpectedCallException), nice mock (umožňuje libovolné volánà metod na tomto objektu - u neinstruovaných metod vracà "výchozÃ" hodnoty - null, 0 nebo false).
V druhé verzi je také novinkou Class Extension knihovna, která vám umožňuje generovat mock objekty nikoliv pouze na základÄ› rozhranÃ, ale i na základÄ› normálnÃch tÅ™Ãd (tzn. nenà potÅ™eba mÃt definovaný interface). Tento způsob vám také umožnà nechat nÄ›které implementace metod mock objektů nepÅ™ekryté knihovnou EasyMock - tzn. budou obsahovat původnà logiku reálné tÅ™Ãdy.
Testy jsou pomÄ›rnÄ› velmi pÅ™ehledné, jednoduÅ¡e se pÃÅ¡ou a uÅ¡etřà vám plno práce s nastavovánÃm prostÅ™edà pro test. NavÃc vám umožnà testovat funkcionalitu, která je jen velmi obtÞnÄ› testovatelná (napÅ™. že váš skript odeÅ¡le v urÄitou chvÃli email).
Je potÅ™eba si uvÄ›domit, že objekty, které nahrazujete se vlastnÄ› netestujÃ. Použità mock objektů vám umožňuje vytvářenà izolovaných unit testů - ty jsou sice jednoduššà a pÅ™ehlednÄ›jÅ¡Ã, ale nemajà takovou cenu pro vlastnà testovánà jako testy integraÄnÃ. IntegraÄnà testy vám pomohou objevit chyby na úrovni rozhranà různých komponent - tzn. ne pouze, komponenta A se chová tak jak má, ale že komponenta B použÃvá komponentu A jak má. Ve výsledku totiž uživatel aplikace nepracuje pouze s komponentami jednotlivÄ›, ale s celým systémem komponent a chyba komunikace mezi komponentami je pro nÄ›j úplnÄ› stejná jako chyba jedné konkrétnà komponenty. ProstÄ› to nefunguje.
Zdrojové kódy pÅ™Ãkladu ke staženà jako projekt Maven 2
Mock Objects - úvod do techniky mockovánÃ
EasyMock - knihovna pro podporu dynamických mock objektů (viz. pÅ™Ãklady v Älánku)
jMock - alternativa k EasyMock, abyste si mohli vybrat ![]()












Loading ...
01.22.2007 v 15:14
Velmi pekny clanok.
Novoj:Ale mam problem s pochopenim testu c. 2. Ako to dopadne pri verify() ? Zliha to,a lebo je to OK ? Nepochopil som presne ako sa zachova konstrukcia: expectLastCall().andThrow(new SenderException) . Vysvetli mi to niekto? Vdaka.
01.22.2007 v 17:12
VÅ¡echny testy ve zdrojových kódech projdou bez chyby. V metodÄ› verify se skuteÄnÄ› ověřuje pouze to, co jsem uvedl v bodÄ› #6. Konstrukce “expectLastCall().andThrow(new SenderException())” Å™Ãká pÅ™esnÄ› toto. Výsledek poslednÃho instruovaného volánà (což je v naÅ¡em pÅ™ÃpadÄ› #4) bude exception, která je uvedená v parametru.
Tzn. když je zavolána objektem tested metoda performBusinessLogic (#8) a ten uvnitÅ™ svého vykonávánà zavolá na messageMocku metodu send, EasyMock mÃsto “simulace” vykonánà vyhodà exception SenderException.
ObdobnÄ› by se dal mock instruovat k vrácenà návratové hodnoty (pokud by metoda send mÄ›la nÄ›jakou návratovou hodnotu, jako že v naÅ¡em ukázkovém pÅ™ÃkladÄ› nemá) deklaracà (uvažujme boolean):
expectLastCall().andReturn(Boolean.TRUE)
Pokud by stále byly nÄ›jaké pochybnosti, doporuÄuji stáhnout si pÅ™iložený projekt a celé si to oddebugovat. Tam nejlépe poznáte, co se kdy jak volá a s jakými výsledky.
A propo. DÄ›kuji za kladnou reakci - taková vÄ›c ÄlovÄ›ka vždycky potěšÃ.
Satai:01.22.2007 v 19:56
Ajaj, také jsem se chystal napsat něco o EasyMock. Objevil jsem je před nedávnem a dost mi zjednodušily život.
Jsem Å edý » Blog Archive » Mock testing - PotÄ›mkinovy vesnice:VÄ›tÅ¡inou testuji stav, ale nÄ›kdy je opravdu lepšà testovat procesy a na to je EasyMock jako dÄ›laný. NaÅ¡el jsem i dalšà knihovny jako je JMock, ale pÅ™iÅ¡ly mi pÅ™ÃliÅ¡ komplexnà pro 95% použitÃ. A urÄitÄ› musÃm pochválit EasyMock za žikovné použità Javy5 v v nových versÃch knihovny.
01.22.2007 v 20:21
[...] PÅ™ed pár týdny jsem objevil skvÄ›lou knihovnu EasyMock. Chystal jsem se o nà nÄ›co málo blognout, ale Otec Fura mne pÅ™edbÄ›hnul. RozhodnÄ› si jeho post pÅ™eÄtÄ›te. Nazajde moc do hloubky, ale na prvnà dojem to staÄÃ. [...]
Andrej:01.24.2007 v 15:28
zdrojaky ako png - to je pomerne velka vzacnost!
Novoj:01.24.2007 v 15:53
Nicméně kompletnà zdrojáky je možné si stáhnout jako ZIPko, takže pokud máte zájem o detaily, originály jsou k dispozici.
Old ShatterKuna:01.25.2007 v 10:19
Moc ty mock objekty nechápu. TeÄ mám nÄ›jaké automatické testy, které dynamicky vytvořà SQL dotaz a vyberou nÄ›co z databáze. Já pak potÅ™ebuju zjistit, jestli ten SELECT nÄ›co vracà nebo ne. TÄ›chto testů probÃhá za sebou nÄ›kolik a vždy se hrabe do databáze a tahá to data, což dÄ›lá testy ponÄ›kud zdlouhavými, protože je tam hodnÄ› záznamů. Dá se pro tento problém použÃt nÄ›jaký mock objekt, který dobu provádÄ›nà zkrátÃ? Já si teda myslÃm, že ne, protože zde se jedná o kontrolu dat a ne o kontrolu pÅ™Ãstupu k databázi. Mám pravdu?
Novoj:01.25.2007 v 10:33
Mock objekty se typicky nehodà na testovánà “datové vrstvy” - tzn. pokud potÅ™ebujete testovat, zda jste složil správnÄ› SQL dotaz, tak v takovém pÅ™ÃpadÄ› vám nezbyde než se skuteÄnÄ› dotázat databáze a poÄkat si na reálné výsledky. S tÃm souvisà i nutná pÅ™Ãprava testovacÃch dat.
Mock objekty se hodà na testovánà aplikaÄnà vrstvy (viz. pÅ™Ãklad s business objektem). Vrstva obsahujÃcà aplikaÄnà logiku obvykle pracuje s datovou vrstvou, ze které zÃskává data pro své operace. Za pÅ™edpokladu, že máme již datovou vrstvu otestovanou a funkÄnà - můžeme si uÅ¡etÅ™it práci pÅ™i testovánà aplikaÄnà vrstvy použitÃm mocků.
A) původnà pÅ™Ãstup:
chci testovat aplikaÄnà objekt, ten se dotazuje datové vrstvy -> pÅ™ed vlastnÃm testem musÃm naplnit data v databázi, tak aby, když se aplikaÄnà objekt zeptá datového, vrátily správná data. Je to dost práce, ale testuju tÃm vlastnÄ› najednou nÄ›kolik vÄ›cà (integraÄnà test): vlastnà aplikaÄnà objekt, objekty datové vrstvy, spolupráci aplikaÄnÃho objektu s datovou vrstvou
B) pÅ™Ãstup s mocky:
Tomas Jurman:chci testovat aplikaÄnà objekt, ten se dotazuje datové vrstvy -> v testu si vytvoÅ™Ãm virtuálnà mock objekty datové vrstvy a nainstruuji jim jejich chovánà - tzn. Å™eknu jim, že až se jich aplikaÄnà objekt zeptá, majà vrátit konkrétnà testová data. Žádné dotazy do databáze neprobÄ›hnou. Výsledkem je daleko ménÄ› práce pÅ™i psanà testů a izolovaný (unit) test, pÅ™i kterém testuji jen a jen aplikaÄnà objekt. SamozÅ™ejmÄ› musÃm nÄ›kde jinde otestovat zvlášť datovou vrstvu, abych zamezil chybám na tomto mÃstÄ›. MyÅ¡lenkou je, že i když budu izolovanÄ› psát testy na vÃce objektů (zvlášt aplikaÄnà a zvlášt datová vrstvaj), zabere mi to ménÄ› práce a bude to lépe otestované, než kdybych se pokouÅ¡el psát pouze integraÄnà (složité) testy. V unit testech mám totiž vÄ›tšà možnosti jak otestovat i různé niance daného volánÃ, což bych pÅ™i volánà ob jeden objekt obtÞnÄ› testoval.
10.24.2008 v 20:35
DÃky moc za super Älánek.
bjbj:11.19.2008 v 0:26
Test Ä. 1 chápu - je jasný a srozumitelný. V podstatÄ› testujeme if pÅ™Ãkaz ve výkonné metodÄ› business objektu. Ale test Ä. 2 nechápu - jak autor Älánku správnÄ› pÃÅ¡e, je to simulace, ne test. Kdy ho tedy spouÅ¡tÄ›t ? Když nepojede internet a spustÄ›nà business classy naostro by tedy způsobilo logovánÃ, pustÃme mÃsto toho simulaci (test Ä. 2) s tÃm, že pÅ™edpokládáme, že se spůstà logovánà a v tomto pÅ™ÃpadÄ› prohlásÃme test za úspěšný ? V takovém pÅ™ÃpadÄ› ale test sám o sobÄ› závisà jeÅ¡tÄ› na okolnÃm prostÅ™edà (stavu internetu) a sám se nemůže vyhodnotit jako úspěšný nebo neúspěšný - nebo je to úplnÄ› jinak ???