Razlika između Stuba, ismijavanja i špijuna u Spockovom okviru

1. Pregled

U ovom vodiču, razgovarat ćemo o razlikama između Oponašanje, Stub, i Špijun u Spockovom okviru. Ilustrirat ćemo što okvir nudi u odnosu na ispitivanje temeljeno na interakciji.

Spock je okvir za testiranje za Java i Groovy koji pomaže automatizirati postupak ručnog testiranja softverske aplikacije. Predstavlja vlastite podsmjehe, škripce i špijune te dolazi s ugrađenim mogućnostima za testove koji obično zahtijevaju dodatne knjižnice.

Prvo ćemo ilustrirati kada bismo trebali koristiti klice. Zatim ćemo proći kroz ruganje. Na kraju ćemo opisati nedavno predstavljeno Špijun.

2. Ovisnosti Mavena

Prije nego što započnemo, dodajte naše ovisnosti o Mavenu:

 org.spockframework spock-core 1.3-RC1-groovy-2.5 test org.codehaus.groovy groovy-all 2.4.7 test 

Imajte na umu da će nam trebati 1,3-RC1-groovy-2,5 verzija Spocka. Špijun bit će predstavljen u sljedećoj stabilnoj verziji Spock Framework-a. Sada Špijun dostupan je u prvom izdanju za verziju 1.3.

Za ponovni prikaz osnovne strukture Spockova testa, pogledajte naš uvodni članak o testiranju s Groovyjem i Spockom.

3. Ispitivanje temeljeno na interakciji

Ispitivanje temeljeno na interakciji tehnika je koja nam pomaže testirati ponašanje objekata - konkretno, kako međusobno komuniciraju. Za to možemo koristiti lažne implementacije koje se nazivaju lažne i kriminalne smicalice.

Naravno, zasigurno bismo vrlo lako mogli napisati vlastite implementacije lažnih djela. Problem se pojavljuje kad količina našeg proizvodnog koda raste. Pisanje i održavanje ovog koda ručno postaje teško. Zbog toga koristimo podsmješljive okvire, koji pružaju sažet način kratkog opisivanja očekivanih interakcija. Spock ima ugrađenu potporu za ruganje, trljanje i špijuniranje.

Kao i većina Java knjižnica, Spock koristi JDK dinamički proxy za ruganje sučelja i Byte Buddy ili cglib proxy za ruganje klasa. Stvara lažne implementacije tijekom izvođenja.

Java već ima mnogo različitih i zrelih knjižnica za izrugivanje klasa i sučelja. Iako se svaka od njih može koristiti u Spocku, još uvijek postoji jedan od glavnih razloga zašto bismo trebali koristiti Spockove podsmjehe, škrgu i špijune. Uvođenjem svih ovih u Spock, možemo iskoristiti sve Groovyjeve mogućnosti kako bi naši testovi bili čitljiviji, lakši za pisanje i definitivno zabavniji!

4. Pozivi metodom štucanja

Ponekad, u jediničnim testovima moramo osigurati lažno ponašanje klase. To bi mogao biti klijent vanjske usluge ili klasa koja omogućuje pristup bazi podataka. Ova je tehnika poznata kao trljanje.

Stub je kontrolirana zamjena postojeće klase ovisnost u našem testiranom kodu. Ovo je korisno za upućivanje poziva metode koji reagira na određeni način. Kada koristimo stub, ne zanima nas koliko će se puta metoda pozivati. Umjesto toga, samo želimo reći: vratite ovu vrijednost kad se pozove s tim podacima.

Prijeđimo na primjer koda s poslovnom logikom.

4.1. Kod koji se ispituje

Stvorimo klasu modela tzv Artikal:

predmet javne klase {private final String id; privatni konačni naziv niza; // standardni konstruktor, getteri, equals}

Moramo nadjačati jednako (Objekt drugo) metoda kako bi naše tvrdnje funkcionirale. Spock će upotrijebiti jednako tijekom tvrdnji kada koristimo dvostruki znak jednakosti (==):

nova stavka ('1', 'name') == nova stavka ('1', 'name')

Ajmo sada stvoriti sučelje ItemProvider jednom metodom:

javno sučelje ItemProvider {List getItems (List itemIds); }

Trebat će nam i klasa koja će biti testirana. Mi ćemo dodati ItemProvider kao ovisnost u ItemService:

javna klasa ItemService {privatni konačni ItemProvider itemProvider; javna ItemService (ItemProvider itemProvider) {this.itemProvider = itemProvider; } Popis getAllItemsSortedByName (Popis itemIds) {Popis stavki = itemProvider.getItems (itemIds); vratiti items.stream (). sortirano (Comparator.comparing (Item :: getName)) .collect (Collectors.toList ()); }}

Želimo da naš kod ovisi o apstrakciji, a ne o određenoj implementaciji. Zato koristimo sučelje. Ovo može imati mnogo različitih implementacija. Na primjer, mogli smo čitati stavke iz datoteke, stvoriti HTTP klijent vanjskoj usluzi ili čitati podatke iz baze podataka.

U ovom kodu, trebat ćemo zaustaviti vanjsku ovisnost, jer želimo samo testirati našu logiku sadržanu u getAllItemsSortedByName metoda.

4.2. Upotreba zabodenog predmeta u ispitnom kodu

Inicirajmo ItemService objekt u postaviti() metoda pomoću a Stub za ItemProvider ovisnost:

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

Sada, idemo napraviti itemProvider vrati popis stavki na svaki poziv s određenim argumentom:

itemProvider.getItems (['offer-id', 'offer-id-2']) >> [new Item ('offer-id-2', 'Zname'), new Item ('offer-id', 'Aname ')]

Koristimo >> operand za ublažavanje metode. The getItems metoda uvijek vraća popis dviju stavki kad se pozove s ['Ponuda-id', 'ponuda-id-2'] popis. [] je Groovy prečac za stvaranje popisa.

Evo cijele metode ispitivanja:

def 'trebao bi vratiti stavke poredane po nazivu' () {dato: def ids = ['offer-id', 'offer-id-2'] itemProvider.getItems (ids) >> [new Item ('offer-id-2 ',' Zname '), nova Stavka (' offer-id ',' Aname ')] kada: Lista stavki = itemService.getAllItemsSortedByName (ids) zatim: items.collect {it.name} == [' Aname ',' Zname ']}

Mnogo je više mogućnosti sabijanja koje možemo koristiti, kao što su: korištenje ograničenja za podudaranje argumenata, korištenje sekvenci vrijednosti u stubovima, definiranje različitog ponašanja u određenim uvjetima i odgovaranje odgovora metode.

5. Podrugljive metode razreda

Sada, razgovarajmo o podrugljivim klasama ili sučeljima u Spocku.

Ponekad, željeli bismo znati je li pozvana neka metoda ovisnog objekta s navedenim argumentima. Želimo se usredotočiti na ponašanje predmeta i istražiti njihove interakcije gledajući pozive metode.Ruganje je opis obvezne interakcije između predmeta u testnoj klasi.

Testirat ćemo interakcije u primjeru koda koji smo opisali u nastavku.

5.1. Šifra s interakcijom

Za jednostavan primjer spremit ćemo stavke u bazu podataka. Nakon uspjeha želimo objaviti događaj na brokeru poruka o novim stavkama u našem sustavu.

Primjer posrednika poruka je RabbitMQ ili Kafka, tako da ćemo općenito opisati naš ugovor:

javno sučelje EventPublisher {void objavljivanje (String addedOfferId); }

Naša metoda ispitivanja spremit će neprazne stavke u bazu podataka, a zatim objaviti događaj. Spremanje stavke u bazu podataka u našem je primjeru irelevantno, pa ćemo samo dati komentar:

void saveItems (List itemIds) {List notEmptyOfferIds = itemIds.stream () .filter (itemId ->! itemId.isEmpty ()) .collect (Collectors.toList ()); // spremanje u bazu podataka notEmptyOfferIds.forEach (eventPublisher :: objavljivanje); }

5.2. Provjera interakcije s ismijanim objektima

Ajmo sada testirati interakciju u našem kodu.

Prvi, trebamo se rugati EventPublisher u našem postaviti() metoda. U osnovi, mi kreiramo novo polje instance i ismijavamo ga pomoću Ismijavanje (razred) funkcija:

klasa ItemServiceTest proširuje specifikaciju {ItemProvider itemProvider ItemService itemService EventPublisher eventPublisher def setup () {itemProvider = Stub (ItemProvider) eventPublisher = Mock (EventPublisher) itemService = new ItemService (itemProvider, eventPublisher)}

Sada možemo napisati našu metodu ispitivanja. Proći ćemo 3 žice: ”,‘ a ’,‘ b ’i očekujemo da je naša eventPublisher objavit će 2 događaja sa žicama „a“ i „b“:

def 'trebao bi objaviti događaje o novim spremljenim praznim ponudama' () {dato: def offerIds = ['', 'a', 'b'] kada: itemService.saveItems (offerIds) tada: 1 * eventPublisher.publish (' a ') 1 * eventPublisher.publish (' b ')}

Pogledajmo bliže našu tvrdnju u finalu zatim odjeljak:

1 * eventPublisher.publish ('a')

Mi to očekujemo itemService nazvat će eventPublisher.publish (niz) s argumentom "a".

Zapinjajući, govorili smo o ograničenjima argumenata. Ista se pravila primjenjuju na podsmijehe. To možemo provjeriti eventPublisher.publish (niz) pozvan je dva puta s bilo kojim ne-null i non-empty argumentom:

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

5.3. Kombiniranje ruganja i stubiranja

U Spock, a Oponašanje može se ponašati isto kao i Stub. Tako možemo ismijanim objektima reći da bi za poziv određene metode trebao vratiti zadane podatke.

Zamijenimo ItemProvider s Ismijavanje (razred) i stvoriti novi ItemService:

dato: itemProvider = Ismijavanje (ItemProvider) itemProvider.getItems (['item-id']) >> [new Item ('item-id', 'name')] itemService = new ItemService (itemProvider, eventPublisher) kada: def items = itemService.getAllItemsSortedByName (['item-id']), zatim: items == [new Item ('item-id', 'name')] 

Mozemo prepisati trljanje iz dato odjeljak:

1 * itemProvider.getItems (['id-predmeta']) >> [nova stavka ('id-predmeta', 'ime')]

Dakle, ovaj redak kaže: itemProvider.getItems pozvat će se jednom s [‘Item-‘id '] argument i povratak zadanog niza.

Već znamo da se rugalice mogu ponašati isto kao i krnje. Sva pravila koja se odnose na ograničenja argumenata, vraćanje višestrukih vrijednosti i nuspojave također se odnose na Oponašanje.

6. Satovi špijuniranja u Spocku

Špijuni pružaju mogućnost omotavanja postojećeg predmeta. To znači da možemo slušati razgovor između pozivatelja i stvarnog objekta, ali zadržati izvorno ponašanje objekta. U osnovi, Špijun delegira pozive metode izvornom objektu.

Za razliku od Oponašanje i Stub, ne možemo stvoriti Špijun na sučelju. Omotava stvarni objekt, pa ćemo dodatno morati proslijediti argumente za konstruktor. U suprotnom, pozvat će se zadani konstruktor tipa.

6.1. Kod koji se ispituje

Stvorimo jednostavnu implementaciju za EventPublisher. LoggingEventPublisher ispisat će u konzoli id ​​svake dodane stavke. Evo implementacije metode sučelja:

@Override public voidobjavi (String addedOfferId) {System.out.println ("Objavio sam:" + addedOfferId); }

6.2. Testiranje sa Špijun

Stvaramo špijune na sličan način ismijavanja i šaranja, koristeći Špijun (razred) metoda. LoggingEventPublisher nema nikakve druge ovisnosti klase, tako da ne moramo prosljeđivati ​​argumente konstruktora:

eventPublisher = Špijun (LoggingEventPublisher)

Ajmo sada testirati našeg špijuna. Treba nam nova instanca ItemService s našim špijuniranim objektom:

dato: eventPublisher = Spy (LoggingEventPublisher) itemService = new ItemService (itemProvider, eventPublisher) kada: itemService.saveItems (['item-id']) tada: 1 * eventPublisher.publish ('item-id')

Potvrdili smo da eventPublisher.publish metoda je pozvana samo jednom. Uz to, poziv metode proslijeđen je stvarnom objektu, pa ćemo vidjeti izlaz println u konzoli:

Objavio sam: id-id

Imajte na umu da kada koristimo stub na metodi Špijun, tada neće pozvati metodu stvarnog objekta. Općenito, trebali bismo izbjegavati korištenje špijuna. Ako to moramo učiniti, možda bismo trebali preurediti kod prema specifikaciji?

7. Dobri jedinični testovi

Završimo s kratkim sažetkom kako upotreba ismijanih predmeta poboljšava naše testove:

  • stvaramo deterministička testna skupa
  • nećemo imati nikakve nuspojave
  • naši će jedinični testovi biti vrlo brzi
  • možemo se usredotočiti na logiku sadržanu u jednoj Java klasi
  • naši testovi su neovisni o okolini

8. Zaključak

U ovom smo članku temeljito opisali špijune, lažne i kriminalne napade u Groovyju. Znanje o ovoj temi učinit će naše testove bržim, pouzdanijim i lakšim za čitanje.

Provedba svih naših primjera može se naći u projektu Github.


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