Rozdiel medzi Stub, Mock a Spy v Spock Framework

1. Prehľad

V tomto návode ideme diskutovať o rozdieloch medzi Mock, Pahýľa Špión v rámci Spock. Ukážeme si, čo rámec ponúka vo vzťahu k testovaniu založenému na interakciách.

Spock je testovací rámec pre Java a Groovy ktorý pomáha automatizovať proces manuálneho testovania softvérovej aplikácie. Predstavuje svoje vlastné posmechy, pahýly a špiónov a prichádza so vstavanými funkciami pre testy, ktoré zvyčajne vyžadujú ďalšie knižnice.

Najskôr si ukážeme, kedy by sme mali použiť pahýly. Potom si urobíme posmech. Na záver si popíšeme nedávno predstavené Špión.

2. Maven závislosti

Než začneme, pridajme naše závislosti Maven:

 org.spockframework testovacie jadro 1.3-RC1-groovy-2.5 test org.codehaus.groovy groovy-all 2.4.7 test 

Upozorňujeme, že budeme potrebovať 1.3-RC1-groovy-2.5 verzia Spock. Špión bude predstavená v nasledujúcej stabilnej verzii Spock Framework. Práve teraz Špión je k dispozícii v prvom kandidátovi na verziu 1.3.

Rekapituláciu základnej štruktúry Spockovho testu nájdete v našom úvodnom článku o testovaní pomocou nástrojov Groovy a Spock.

3. Testovanie na základe interakcie

Interakčné testovanie je technika, ktorá nám pomáha testovať správanie objektov - konkrétne to, ako navzájom interagujú. Na tento účel môžeme použiť atrapy implementácií, ktoré sa nazývajú falošné a podobné pokusy.

Samozrejme, určite by sme mohli veľmi ľahko napísať svoje vlastné implementácie falošných správ a pňov. Problém sa objaví, keď narastie množstvo nášho produkčného kódu. Ručné písanie a údržba tohto kódu je zložitá. Preto používame zosmiešňujúce rámce, ktoré poskytujú stručný spôsob stručného popisu očakávaných interakcií. Spock má vstavanú podporu pre zosmiešňovanie, pichanie a špehovanie.

Rovnako ako väčšina knižníc Java, aj Spock používa na zosmiešňovanie rozhraní dynamický proxy server JDK a na zosmiešňovanie tried proxy Byte Buddy alebo cglib. Za behu vytvára falošné implementácie.

Java už má veľa rôznych a vyspelých knižníc na zosmiešňovanie tried a rozhraní. Aj keď každý z nich sa dá v Spocku použiť, stále existuje jeden hlavný dôvod, prečo by sme mali používať Spockových posmešky, pahýly a špiónov. Zavedením všetkých týchto vecí do Spocku môžeme využiť všetky schopnosti Groovy aby boli naše testy čitateľnejšie, ľahšie sa písali a určite boli zábavnejšie!

4. Hovory metódou stubbing

Niekedy v jednotkových testoch musíme poskytnúť atrapu správania triedy. Môže to byť klient pre externú službu alebo trieda, ktorá poskytuje prístup k databáze. Táto technika je známa ako pichnutie.

Pahýľ je kontrolovateľná náhrada existujúcej triedy závislosť v našom testovanom kóde. To je užitočné pri uskutočňovaní volania metódy, ktoré reaguje určitým spôsobom. Keď používame stub, je nám úplne jedno, koľkokrát bude metóda vyvolaná. Namiesto toho chceme iba povedať: vrátiť túto hodnotu, keď sa volá s týmito údajmi.

Prejdime k ukážkovému kódu s obchodnou logikou.

4.1. Testovaný kód

Vytvorme modelovú triedu s názvom Položka:

verejná trieda Položka {private final String id; súkromné ​​konečné meno reťazca; // štandardný konštruktor, getre, rovná sa}

Musíme prekonať rovná sa (Objekt iné) aby naše tvrdenia fungovali. Spock použije rovná sa počas tvrdení, keď používame dvojité znamienko rovnosti (==):

nová položka ('1', 'meno') == nová položka ('1', 'meno')

Teraz vytvorme rozhranie ItemProvider jednou metódou:

verejné rozhranie ItemProvider {List getItems (List itemIds); }

Budeme tiež potrebovať triedu, ktorá bude testovaná. Pridáme ItemProvider ako závislosť v Služba položky:

verejná trieda ItemService {private final ItemProvider itemProvider; public ItemService (ItemProvider itemProvider) {this.itemProvider = itemProvider; } Zoznam getAllItemsSortedByName (Zoznam itemIds) {Zoznam položiek = itemProvider.getItems (itemIds); vrátiť items.stream (). roztriedené (Comparator.comparing (Item :: getName)) .collect (Collectors.toList ()); }}

Chceme, aby náš kód závisel skôr od abstrakcie ako od konkrétnej implementácie. Preto používame rozhranie. Môže to mať veľa rôznych implementácií. Mohli by sme napríklad čítať položky zo súboru, vytvoriť klienta HTTP pre externú službu alebo čítať údaje z databázy.

V tomto kóde budeme musieť potlačiť externú závislosť, pretože chceme otestovať iba našu logiku obsiahnutú v getAllItemsSortedByName metóda.

4.2. Použitie ukradnutého objektu v testovanom kóde

Poďme inicializovať ItemService objekt v nastaviť() metóda využívajúca a Pahýľ pre ItemProvider závislosť:

ItemProvider itemProvider ItemService itemService def setup () {itemProvider = Stub (ItemProvider) itemService = nový ItemService (itemProvider)}

Teraz, urobme itemProvider vráti zoznam položiek pri každom vyvolaní s konkrétnym argumentom:

itemProvider.getItems (['offer-id', 'offer-id-2']) >> [nová položka ('offer-id-2', 'Zname'), nová položka ('offer-id', 'Aname ')]

Na ukončenie metódy používame >> operand. The getItems metóda vždy vráti zoznam dvoch položiek, keď je volaná s [‘Offer-id ',‘ offer-id-2'] zoznam. [] je a Groovy odkaz na vytváranie zoznamov.

Tu je celá skúšobná metóda:

def 'by mal vrátiť položky zoradené podľa názvu' () {given: def ids = ['offer-id', 'offer-id-2'] itemProvider.getItems (ids) >> [nová položka ('offer-id-2 ',' Zname '), new Item (' offer-id ',' Aname ')] when: List items = itemService.getAllItemsSortedByName (ids) then: items.collect {it.name} == [' Aname ',' Meno ']}

Existuje oveľa viac možností stubingu, ktoré môžeme použiť, napríklad: použitie obmedzení priraďovania argumentov, použitie sekvencií hodnôt v stuboch, definovanie odlišného správania za určitých podmienok a reakcie metód reťazenia.

5. Metódy zosmiešňovania

Teraz si povieme niečo o posmešných triedach alebo rozhraniach v Spocku.

Niekedy by nás zaujímalo, či bola volaná nejaká metóda závislého objektu so zadanými argumentmi. Chceme sa zamerať na správanie objektov a preskúmať, ako interagujú, sledovaním volaní metód.Mocking je popis povinnej interakcie medzi objektmi v testovacej triede.

Interakcie otestujeme v príklade kódu, ktorý sme opísali nižšie.

5.1. Kód s interakciou

Pre jednoduchý príklad ideme ukladať položky do databázy. Po úspechu chceme v sprostredkovateľovi správ zverejniť udalosť o nových položkách v našom systéme.

Príkladom sprostredkovateľa správ je RabbitMQ alebo Kafka, takže všeobecne popíšeme našu zmluvu:

verejné rozhranie EventPublisher {void publish (String addedOfferId); }

Naša testovacia metóda uloží neprázdne položky do databázy a potom udalosť zverejní. Uloženie položky do databázy je v našom príklade irelevantné, preto vložíme iba komentár:

void saveItems (List itemIds) {List notEmptyOfferIds = itemIds.stream () .filter (itemId ->! itemId.isEmpty ()) .collect (Collectors.toList ()); // uložiť do databázy notEmptyOfferIds.forEach (eventPublisher :: publish); }

5.2. Overenie interakcie s vysmievanými objektmi

Teraz otestujme interakciu v našom kóde.

Najprv, musíme sa vysmievať Vydavateľ udalostí v našom nastaviť() metóda. Takže v zásade vytvoríme nové pole inštancie a zosmiešňujeme ho pomocou Mock (Class) funkcia:

trieda ItemServiceTest rozširuje Špecifikácia {ItemProvider itemProvider ItemService itemService EventPublisher eventPublisher def setup () {itemProvider = Stub (ItemProvider) eventPublisher = Mock (EventPublisher) itemService = nový ItemService (itemProvider, eventPublisher)}

Teraz môžeme napísať našu testovaciu metódu. Prejdeme 3 reťazce: ”,‘ a ’,‘ b ’a očakávame, že náš eventPublisher zverejní 2 podujatia s reťazcami „a“ a „b“:

def 'by mal zverejňovať udalosti o nových neprázdnych uložených ponukách' () {given: def offerIds = ['', 'a', 'b'] keď: itemService.saveItems (offerIds) potom: 1 * eventPublisher.publish (' a ') 1 * eventPublisher.publish (' b ')}

Poďme sa bližšie pozrieť na naše tvrdenie vo finále potom časť:

1 * eventPublisher.publish ('a')

Očakávame to itemService zavolá eventPublisher.publish (reťazec) s argumentom „a“.

Pri tvrdom postupe sme hovorili o obmedzeniach argumentov. Rovnaké pravidlá platia aj pre falošné správy. Môžeme to overiť eventPublisher.publish (reťazec) bol volaný dvakrát s akýmkoľvek nenulovým a neprázdnym argumentom:

2 * eventPublisher.publish ({it! = Null &&! It.isEmpty ()})

5.3. Kombinácia zosmiešňovania a pichania

V Spock, a Mock sa môžu správať rovnako ako a Pahýľ. Môžeme teda povedať zosmiešňovaným objektom, že pre dané volanie metódy by malo dané dáta vrátiť.

Prepíšeme an ItemProvider s Mock (Class) a vytvoriť nový ItemService:

zadané: itemProvider = Mock (ItemProvider) itemProvider.getItems (['item-id']) >> [new Item ('item-id', 'name')] itemService = nový ItemService (itemProvider, eventPublisher), keď: def položky = itemService.getAllItemsSortedByName (['item-id']) potom: items == [nová položka ('item-id', 'name')] 

Môžeme prepísať útržky z daný časť:

1 * itemProvider.getItems (['item-id']) >> [nová položka ('item-id', 'name')]

Všeobecne teda tento riadok hovorí: itemProvider.getItems bude volaný raz s [‘Item-‘id’] argument a vráti dané pole.

Už vieme, že falošné správy sa môžu správať rovnako ako pahýle. Všetky pravidlá týkajúce sa obmedzenia argumentov, vrátenia viacerých hodnôt a vedľajších účinkov sa tiež vzťahujú na Mock.

6. Špehovanie tried v Spockovi

Špióni poskytujú schopnosť zabaliť existujúci objekt. To znamená, že môžeme počúvať rozhovor medzi volajúcim a skutočným objektom, ale zachovať si pôvodné správanie objektu. V podstate Špión deleguje volanie metód na pôvodný objekt.

Na rozdiel od Mock a Pahýľ, nemôžeme vytvoriť Špión na rozhraní. Zabalí skutočný objekt, takže navyše budeme musieť odovzdať argumenty pre konštruktor. V opačnom prípade sa vyvolá predvolený konštruktor typu.

6.1. Testovaný kód

Vytvorme jednoduchú implementáciu pre Vydavateľ udalostí. LoggingEventPublisher vypíše v konzole ID každej pridanej položky. Tu je implementácia metódy rozhrania:

@Override public void publish (reťazec addedOfferId) {System.out.println ("Publikoval som:" + addedOfferId); }

6.2. Testovanie s Špión

Špiónov vytvárame podobne ako falošné alebo pahýľové kúsky pomocou Špión (trieda) metóda. LoggingEventPublisher nemá žiadne ďalšie závislosti na triedach, takže nemusíme odovzdávať argumenty konštruktora:

eventPublisher = Spy (LoggingEventPublisher)

Teraz otestujme nášho špióna. Potrebujeme nový príklad ItemService s našim špehovaným objektom:

zadané: eventPublisher = Spy (LoggingEventPublisher) itemService = nový ItemService (itemProvider, eventPublisher), keď: itemService.saveItems (['item-id']) potom: 1 * eventPublisher.publish ('item-id')

Overili sme, že eventPublisher.publish metóda bola volaná iba raz. Volanie metódy bolo navyše odovzdané skutočnému objektu, takže uvidíme výstup z println v konzole:

Publikoval som: item-id

Všimnite si, že keď použijeme stub na metóde Špión, potom nebude volať metódu skutočného objektu. Všeobecne by sme sa mali vyhnúť použitiu špiónov. Ak to musíme urobiť, možno by sme mali zmeniť usporiadanie kódu podľa špecifikácie?

7. Dobré testy jednotky

Na záver skončíme krátkym zhrnutím toho, ako vylepšenie našich testov zlepšuje použitie posmievaných objektov:

  • vytvárame deterministické testovacie súbory
  • nebudeme mať žiadne vedľajšie účinky
  • naše jednotkové testy budú veľmi rýchle
  • môžeme sa zamerať na logiku obsiahnutú v jednej triede Java
  • naše testy sú nezávislé od prostredia

8. Záver

V tomto článku sme dôkladne popísali špiónov, výsmechov a pňov v Groovy. Vďaka vedomostiam v tejto oblasti budú naše testy rýchlejšie, spoľahlivejšie a ľahšie čitateľné.

Implementáciu všetkých našich príkladov možno nájsť v projekte Github.


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