Programovanie

Mocks And Stubs - Understanding Test Doubles With Mockito

Bežná vec, s ktorou sa stretávam, je, že tímy používajúce zosmiešňujúci rámec predpokladajú, že sa vysmievajú.

Nie sú si vedomí, že simulácie sú iba jedným z mnohých testovacích dvojíc, ktoré Gerard Meszaros kategorizoval na xunitpatterns.com.

Je dôležité si uvedomiť, že každý typ testovacieho dvojníka má pri testovaní inú rolu. Rovnakým spôsobom, ako sa musíte naučiť rôzne vzorce alebo spôsoby refaktoringu, musíte pochopiť primitívne úlohy každého typu testovacieho dvojníka. Tieto možno potom kombinovať, aby sa dosiahli vaše testovacie potreby.

Pokryjem veľmi krátku históriu toho, ako k tejto klasifikácii došlo, a ako sa jednotlivé typy líšia.

Urobím to pomocou niekoľkých krátkych a jednoduchých príkladov v aplikácii Mockito.

Už roky ľudia píšu odľahčené verzie systémových komponentov, aby pomohli s testovaním. Všeobecne sa to nazývalo stubbing. V roku 2000 bol v článku „Endo-Testovanie: Testovanie jednotiek s simulovanými objektmi“ predstavený koncept simulovaného objektu. Odvtedy boli pahýly, makety a množstvo ďalších typov testovacích predmetov klasifikovaných Meszarosom ako testovacie dvojice.

Na túto terminológiu sa odvoláva Martin Fowler v publikácii „Mocks Arn't Stubs“ a je prijatá v rámci komunity Microsoftu, ako je uvedené v časti „Exploring The Continuum of Test Doubles“.

Odkaz na každý z týchto dôležitých článkov je uvedený v referenčnej časti.

Vyššie uvedený diagram zobrazuje bežne používané typy testovacích dvojitých testov. Nasledujúca adresa URL poskytuje dobrý krížový odkaz na každý zo vzorov a ich vlastností, ako aj alternatívnu terminológiu.

//xunitpatterns.com/Test%20Double.html

Mockito je testovací špiónsky rámec a je veľmi ľahké sa ho naučiť. Pozoruhodné pre Mockita je, že očakávania akýchkoľvek falošných objektov nie sú definované pred testom, ako to niekedy býva v iných falošných rámcoch. To vedie k prirodzenejšiemu štýlu (IMHO), keď sa začnete vysmievať.

Nasledujúce príklady slúžia iba na demonštráciu použitia programu Mockito na implementáciu rôznych typov testovacích dvojíc.

Na webovej stránke existuje oveľa väčšie množstvo konkrétnych príkladov, ako používať Mockito.

//docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html

Ďalej uvádzame niekoľko základných príkladov použitia nástroja Mockito na znázornenie úlohy každého testovacieho dvojníka, ako ho definoval Meszaros.

Pre každú z nich som uviedol odkaz na hlavnú definíciu, aby ste mohli získať viac príkladov a úplnú definíciu.

//xunitpatterns.com/Dummy%20Object.html

Toto je najjednoduchšia zo všetkých testovacích dvojhier. Toto je objekt, ktorý nemá implementáciu a ktorý sa používa výlučne na vyplnenie argumentov volaní metód, ktoré pre váš test nie sú relevantné.

Napríklad nižšie uvedený kód používa veľa kódu na vytvorenie zákazníka, čo nie je pre test dôležité.

Testu nezáležalo na tom, ktorý zákazník je pridaný, pokiaľ sa počet zákazníkov vráti ako jeden.

public Customer createDummyCustomer () {County county = new County ("Essex"); Mesto mesto = nové mesto ("Romford", okres); Adresa adresa = nová adresa („1234 Bank Street“, mesto); Zákazník zákazník = nový Zákazník ("john", "dobie", adresa); spätný zákazník; } @Test public void addCustomerTest () {Customer dummy = createDummyCustomer (); AddressBook addressBook = nový AddressBook (); addressBook.addCustomer (figurína); assertEquals (1, addressBook.getNumberOfCustomers ()); } 

Vlastne sa nestaráme o obsah objektu zákazníka - ale je to potrebné. Môžeme vyskúšať nulovú hodnotu, ale ak je kód správny, čakali by ste, že bude vyvolaná nejaká výnimka.

@Test (očakáva sa = Exception.class) public void addNullCustomerTest () {Customer dummy = null; AddressBook addressBook = nový AddressBook (); addressBook.addCustomer (figurína); } 

Aby sme tomu zabránili, môžeme na dosiahnutie požadovaného správania použiť jednoduchú figurínu Mockito.

@Test public void addCustomerWithDummyTest () {Customer dummy = mock (Customer.class); AddressBook addressBook = nový AddressBook (); addressBook.addCustomer (figurína); Assert.assertEquals (1, addressBook.getNumberOfCustomers ()); } 

Je to tento jednoduchý kód, ktorý vytvára atrapu objektu, ktorý sa má odovzdať do hovoru.

Figurína zákazníka = falošná (Customer.class);

Nenechajte sa oklamať falošnou syntaxou - tu sa hrá rolu figuríny, nie falošnej.

Rozlišuje to úloha testovacieho dvojníka, nie syntax použitá na jeho vytvorenie.

Táto trieda slúži ako jednoduchá náhrada za zákaznícku triedu a umožňuje veľmi ľahké prečítanie testu.

//xunitpatterns.com/Test%20Stub.html

Úlohou testovacieho pahýľa je vrátiť kontrolované hodnoty testovanému objektu. Tieto sa popisujú ako nepriame vstupy do testu. Dúfajme, že príklad objasní, čo to znamená.

Vezmite si nasledujúci kód

verejná trieda SimplePricingService implementuje PricingService {PricingRepository úložisko; public SimplePricingService (PricingRepository pricingRepository) {this.repository = pricingRepository; } @Override public Price priceTrade (Trade trade) {return repository.getPriceForTrade (trade); } @Override public Price getTotalPriceForTrades (inkasné obchody) {Price totalPrice = new Price (); pre (Trade trade: trades) {Price tradePrice = repository.getPriceForTrade (obchod); totalPrice = totalPrice.add (tradePrice); } návratnosť totalPrice; } 

SimplePricingService má jeden spolupracujúci objekt, ktorým je archív obchodných údajov. Archív obchodných údajov poskytuje obchodné ceny cenovej službe prostredníctvom metódy getPriceForTrade.

Aby sme mohli otestovať logiku businees v SimplePricingService, musíme tieto nepriame vstupy ovládať

tj vstupy, ktoré sme nikdy neprešli testom.

Toto je zobrazené nižšie.

V nasledujúcom príklade vložíme PricingRepository, aby sme vrátili známe hodnoty, ktoré sa dajú použiť na testovanie obchodnej logiky SimpleTradeService.

@Test public void testGetHighestPricedTrade () vyvolá Výnimku {Cena cena1 = nová Cena (10); Cena cena2 = nová Cena (15); Cena cena3 = nová Cena (25); PricingRepository pricingRepository = falošný (PricingRepository.class); when (pricingRepository.getPriceForTrade (any (Trade.class))) .thenReturn (price1, price2, price3); Služba PricingService = nová SimplePricingService (pricingRepository); Cena najvyššiaCena = service.getHighestPricedTrade (getTrades ()); assertEquals (price3.getAmount (), najvyššia cena.getAmount ()); } 

Príklad Sabotéra

Existujú 2 bežné varianty testovacích pahýlov: Responder’s a Saboteur's.

Respondenti sa používajú na testovanie šťastnej cesty ako v predchádzajúcom príklade.

Ako je uvedené nižšie, na testovanie výnimočného správania sa používa sabotér.

@Test (očakáva sa = TradeNotFoundException.class) public void testInvalidTrade () vyvolá výnimku {Trade trade = new FixtureHelper (). GetTrade (); TradeRepository tradeRepository = falošný (TradeRepository.class); keď (tradeRepository.getTradeById (anyLong ())). potom Throw (nový TradeNotFoundException ()); TradingService tradingService = nový SimpleTradingService (tradeRepository); tradingService.getTradeById (trade.getId ()); } 

//xunitpatterns.com/Mock%20Object.html

Vysmievané objekty sa používajú na overenie správania objektu počas testu. Pod správaním objektu myslím, že kontrolujeme, či sa na objekt pri vykonaní testu cvičia správne metódy a cesty.

To sa veľmi líši od podpornej úlohy útržku, ktorý sa používa na poskytnutie výsledkov všetkému, čo testujete.

V pahýli používame vzor definovania návratovej hodnoty pre metódu.

kedy (customer.getSurname ()). thenReturn (priezvisko); 

Falošne kontrolujeme správanie objektu pomocou nasledujúceho formulára.

overiť (listMock) .add (s); 

Tu je jednoduchý príklad, keď chceme vyskúšať, či je nový obchod auditovaný správne.

Tu je hlavný kód.

verejná trieda SimpleTradingService implementuje TradingService {TradeRepository tradeRepository; AuditService auditService; public SimpleTradingService (TradeRepository tradeRepository, AuditService auditService) {this.tradeRepository = tradeRepository; this.auditService = auditService; } verejné Long createTrade (obchodný obchod) hodí CreateTradeException {Long id = tradeRepository.createTrade (obchod); auditService.logNewTrade (obchod); návratové ID; } 

Nasledujúci test vytvorí útržok pre archív obchodných údajov a simulovaný údaj pre AuditService

Potom zavoláme overenie na zosmiešnenej AuditService, aby sme sa uistili, že TradeService to volá

metóda logNewTrade správne

@Mock TradeRepository tradeRepository; @Mock AuditService auditService; @Test public void testAuditLogEntryMadeForNewTrade () vyvolá výnimku {Trade trade = new Trade ("Ref 1", "Description 1"); keď (tradeRepository.createTrade (obchod)). thenReturn (anyLong ()); TradingService tradingService = nový SimpleTradingService (tradeRepository, auditService); tradingService.createTrade (obchod); overiť (auditService) .logNewTrade (obchod); } 

Nasledujúci riadok vykonáva kontrolu vysmievaného AuditService.

overiť (auditService) .logNewTrade (obchod);

Tento test nám umožňuje preukázať, že audítorská služba sa pri vytváraní obchodu správa správne.

//xunitpatterns.com/Test%20Spy.html

Stojí za to pozrieť sa na vyššie uvedený odkaz na presnú definíciu testovacieho špióna.

V aplikácii Mockito ho však rád používam, aby vám umožnil zabaliť skutočný objekt a potom overiť alebo upraviť jeho správanie, aby podporil vaše testovanie.

Tu je príklad, keď sme skontrolovali štandardné správanie zoznamu. Upozorňujeme, že môžeme overiť, či sa volá metóda pridania, a tiež tvrdiť, že položka bola pridaná do zoznamu.

@Spy List listSpy = nový ArrayList (); @Test public void testSpyReturnsRealValues ​​() vyvolá výnimku {String s = "dobie"; listSpy.add (nové reťazce); overiť (listSpy) .add (s); assertEquals (1, listSpy.size ()); } 

Porovnajte to s použitím simulovaného objektu, kde je možné overiť iba volanie metódy. Pretože sa vysmievame iba správaniu zoznamu, nezaznamenáva to, že položka bola pridaná, a vráti predvolenú hodnotu nula, keď voláme metódu size ().

@Mock List listMock = nový ArrayList (); @Test public void testMockReturnsZero () vyvolá výnimku {String s = "dobie"; listMock.add (nové reťazce); overiť (listMock) .add (s); assertEquals (0, listMock.size ()); } 

Ďalšou užitočnou vlastnosťou testSpy je schopnosť potlačiť spätné volania. Keď je to hotové, objekt sa bude správať ako obvykle, kým sa nevytvorí metóda s prístupom.

V tomto príklade zatĺkame metódu get, ktorá vždy vyvolá RuntimeException. Zvyšok správania zostáva rovnaký.

@Test (očakáva sa = RuntimeException.class) public void testSpyReturnsStubbedValues ​​() vyvolá výnimku {listSpy.add (nový reťazec ("dobie")); assertEquals (1, listSpy.size ()); kedy (listSpy.get (anyInt ())). thenThrow (nový RuntimeException ()); listSpy.get (0); } 

V tomto príklade si opäť necháme základné chovanie, ale zmeníme metódu size () tak, aby sa pri všetkých nasledujúcich hovoroch vrátila pôvodne 1 a 5.

test verejnej neplatnosti SpyReturnsStubbedValues2 () vyvolá výnimku {int size = 5; when (listSpy.size ()). thenReturn (1, veľkosť); int mockedListSize = listSpy.size (); assertEquals (1, mockedListSize); mockedListSize = listSpy.size (); assertEquals (5, mockedListSize); mockedListSize = listSpy.size (); assertEquals (5, mockedListSize); } 

To je celkom čarovné!

//xunitpatterns.com/Fake%20Object.html

Falošné predmety sú zvyčajne ručne vyrobené alebo ľahké predmety, ktoré sa používajú iba na testovanie a nie sú vhodné na výrobu. Dobrým príkladom by bola databáza v pamäti alebo falošná vrstva služby.

Majú tendenciu poskytovať oveľa viac funkcií ako štandardné testovacie dvojhry a ako také pravdepodobne nie sú zvyčajne kandidátmi na implementáciu pomocou nástroja Mockito. To neznamená, že by nemohli byť skonštruované ako také, len to asi nestojí za to implementovať týmto spôsobom.

Vyskúšajte dvojité vzory

Endo-Testing: Unit Uniting with Mock Objects

Vysmievané role, nie predmety

Mocks nie sú pahýly

//msdn.microsoft.com/en-us/magazine/cc163358.aspx

Tento príbeh „Mocks And Stubs - Understanding Test Doubles With Mockito“ bol pôvodne publikovaný spoločnosťou JavaWorld.

$config[zx-auto] not found$config[zx-overlay] not found