Uvod u testiranje sa Spockom i Groovyjem

1. Uvod

U ovom ćemo članku pogledati Spock, Groovyjev okvir za testiranje. Spock uglavnom želi biti snažnija alternativa tradicionalnom JUnit stogu, iskorištavajući Groovyjeve značajke.

Groovy je jezik zasnovan na JVM-u koji se neprimjetno integrira s Javom. Uz interoperabilnost, nudi i dodatne jezične koncepte kao što su dinamika, neobavezni tipovi i meta-programiranje.

Koristeći Groovy, Spock uvodi nove i izražajne načine testiranja naših Java aplikacija, što jednostavno nije moguće u uobičajenom Java kodu. Tijekom ovog članka istražit ćemo neke spockove koncepte na visokoj razini, s nekoliko praktičnih primjera korak po korak.

2. Ovisnost Mavena

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

 org.spockframework spock-core 1.0-groovy-2.4 test org.codehaus.groovy groovy-all 2.4.7 test 

Dodali smo i Spocka i Groovyja kao i bilo kojoj standardnoj knjižnici. Međutim, kako je Groovy novi JVM jezik, moramo uključiti i gmavenplus dodatak kako biste ga mogli kompajlirati i pokrenuti:

 org.codehaus.gmavenplus gmavenplus-plugin 1.5 kompajliranje testCompile 

Sada smo spremni napisati naš prvi Spock test, koji će biti napisan u Groovyjevom kodu. Imajte na umu da Groovy i Spock koristimo samo u svrhu testiranja i zbog toga se te ovisnosti testiraju.

3. Struktura Spockova testa

3.1. Specifikacije i značajke

Dok pišemo svoje testove u Groovyju, moramo ih dodati u src / test / groovy direktorij, umjesto src / test / java. Stvorimo svoj prvi test u ovom direktoriju, nazvavši ga Specifikacija.groovy:

klasa FirstSpecification proširuje specifikaciju {}

Imajte na umu da produžujemo Specifikacija sučelje. Svaka klasa Spocka to mora proširiti kako bi joj okvir bio dostupan. Čini to tako što nam omogućuje da implementiramo svoj prvi značajka:

def "jedan plus jedan treba biti jednak dva" () {očekivati: 1 + 1 == 2}

Prije objašnjavanja koda, također je vrijedno napomenuti da je u Spocku ono što nazivamo a značajka donekle je sinonim za ono što vidimo kao test u JUnit. Tako kad god se pozivamo na a značajka mi zapravo mislimo na a test.

Sada, analizirajmo naše značajka. Pritom bismo odmah trebali moći uočiti neke razlike između njega i Jave.

Prva je razlika u tome što je naziv metode obilježja napisan kao obični niz. U JUnit-u bismo imali naziv metode koji koristi kameru ili donje crte za odvajanje riječi, što ne bi bilo toliko izražajno niti čitljivo za ljude.

Sljedeće je da naš testni kod živi u očekivati blok. Uskoro ćemo detaljnije pokriti blokove, ali u osnovi oni su logičan način razdvajanja različitih koraka naših testova.

Napokon, shvaćamo da nema tvrdnji. To je zato što je tvrdnja implicitna i prolazi kada je naša izjava jednaka pravi i neuspjeh kad se izjednači lažno. Ponovno ćemo ukratko detaljnije pokriti tvrdnje.

3.2. Blokovi

Ponekad prilikom pisanja testa JUnit možemo primijetiti da ne postoji ekspresivan način raščlanjivanja na dijelove. Na primjer, ako bismo pratili razvojno usmjeren razvoj, možda bismo na kraju označili s obzirom kad tada dijelovi pomoću komentara:

@Test javna praznina givenTwoAndTwo_whenAdding_thenResultIsFour () {// Dano int prvo = 2; int drugo = 4; // Kada je int rezultat = 2 + 2; // Zatim assertTrue (rezultat == 4)}

Spock ovaj problem rješava blokovima. Blokovi su Spokov izvorni način razbijanja faza našeg testa pomoću naljepnica. Daju nam etikete za s obzirom kad tada i više:

  1. Postaviti (Dodijeljeno s obzirom) - Ovdje izvršavamo sve postavke potrebne prije pokretanja testa. Ovo je implicitni blok, čiji kôd uopće nije niti u jednom bloku
  2. Kada - Ovdje pružamo a podražaj onome što je na ispitu. Drugim riječima, tamo gdje pozivamo našu metodu koja se ispituje
  3. Zatim - Tu pripadaju tvrdnje. U Spocku se to ocjenjuje kao obične logičke tvrdnje, koje će biti obrađene kasnije
  4. Očekivati - Ovo je način izvođenja našeg podražaj i tvrdnja unutar istog bloka. Ovisno o tome što smatramo izražajnijim, ovaj blok možemo ili ne možemo koristiti
  5. Počistiti - Ovdje rušimo sve resurse testne ovisnosti koji bi inače ostali. Na primjer, možda bismo htjeli ukloniti sve datoteke iz datotečnog sustava ili ukloniti testne podatke zapisane u bazu podataka

Pokušajmo ponovno implementirati naš test, ovaj put u potpunosti koristeći blokove:

def "dva plus dva treba biti jednako četiri" () {dato: int lijevo = 2 int desno = 2 kada: int rezultat = lijevo + desno onda: rezultat == 4}

Kao što vidimo, blokovi pomažu našem testu da postane čitljiviji.

3.3. Iskorištavanje Groovyjevih značajki za tvrdnje

Unutar zatim i očekivati blokova, tvrdnje su implicitne.

Uglavnom se svaka izjava procijeni i onda propadne ako nije pravi. Kada se ovo kombinira s raznim Groovy značajkama, to čini dobar posao uklanjajući potrebu za knjižnicom tvrdnji. Pokušajmo popis tvrdnja da se to dokaže:

def "Trebao bi ga moći ukloniti sa popisa" () {dato: def list = [1, 2, 3, 4] kada: list.remove (0), a zatim: list == [2, 3, 4]}

Iako se u ovom članku samo kratko dotičemo Groovyja, vrijedi objasniti što se ovdje događa.

Prvo, Groovy nam daje jednostavnije načine stvaranja popisa. Naše elemente možemo jednostavno prijaviti uglastim zagradama, a interno a popis bit će instanciran.

Drugo, kako je Groovy dinamičan, možemo ga koristiti def što samo znači da ne deklariramo tip za svoje varijable.

Konačno, u kontekstu pojednostavljivanja našeg testa, najkorisnija demonstrirana značajka je preopterećenje operatera. To znači da interno, umjesto uspoređivanja referenci kao u Javi, jednako () Za uspoređivanje dvaju popisa zatražit će se metoda.

Također je vrijedno pokazati što se događa kada naš test ne uspije. Učinimo da se razbije, a zatim pogledajmo što se izlazi na konzolu:

Uvjet nije zadovoljen: popis == [1, 3, 4] | | | false [2, 3, 4] u FirstSpecification. Trebao bi biti u mogućnosti ukloniti s popisa (FirstSpecification.groovy: 30)

Dok sve što se događa zove jednako () na dva popisa, Spock je dovoljno inteligentan da izvrši raščlambu neuspjele tvrdnje, dajući nam korisne informacije za otklanjanje pogrešaka.

3.4. Utvrđivanje iznimaka

Spock nam također pruža ekspresivan način provjere izuzetaka. U JUnit-u neke od naših opcija možda koriste a pokušaj uhvatiti blokirati, izjaviti očekivano na vrhu našeg testa ili koristeći biblioteku treće strane. Spockove izvorne tvrdnje dolaze s načinom rješavanja izuzetaka:

def "Treba ukloniti indeks izvan granica prilikom uklanjanja nepostojeće stavke" () {dato: def list = [1, 2, 3, 4] when: list.remove (20) then: baceno (IndexOutOfBoundsException) list. veličina () == 4}

Ovdje nismo morali uvoditi dodatnu knjižnicu. Još je jedna prednost što bačen () metoda potvrdit će vrstu iznimke, ali neće zaustaviti izvršavanje testa.

4. Testiranje na temelju podataka

4.1. Što je ispitivanje na temelju podataka?

U srži, testiranje vođeno podacima je kada više puta testiramo isto ponašanje s različitim parametrima i tvrdnjama. Klasičan primjer toga bio bi testiranje matematičke operacije kao što je kvadrat broja. Ovisno o raznim permutacijama operanda, rezultat će biti drugačiji. U Javi je pojam koji nam je možda poznatiji parametarsko testiranje.

4.2. Implementacija parametarskog testa u Javi

Za neki kontekst, vrijedi primijeniti parametarski test pomoću JUnit:

@RunWith (Parameterized.class) javna klasa FibonacciTest {@Parameters public static Collection data () {return Arrays.asList (new Object [] [] {{1, 1}, {2, 4}, {3, 9}} ); } privatni int ulaz; očekuje se privatni int; javni FibonacciTest (int ulaz, očekuje se int) {this.input = input; ovo.očekivano = očekivano; } @Test test javne praznine () {assertEquals (fExpected, Math.pow (3, 2)); }}

Kao što vidimo, dosta je više riječi, a kod nije baš čitljiv. Morali smo stvoriti dvodimenzionalni niz objekata koji živi izvan testa, pa čak i objekt omota za ubrizgavanje različitih vrijednosti testa.

4.3. Korištenje podatkovnih tablica u Spocku

Jednostavno pobjeđivanje Spocka u usporedbi s JUnitom je kako on čisto implementira parametrizirane testove. Opet, u Spocku je ovo poznato kao Ispitivanje na temelju podataka. Sada, provedimo opet isti test, samo što ćemo ovaj put koristiti Spock Tablice podataka, koji pruža daleko prikladniji način izvođenja parametriziranog testa:

def "brojevi u dvoje" (int a, int b, int c) 4 3 

Kao što vidimo, imamo samo izravnu i izražajnu tablicu podataka koja sadrži sve naše parametre.

Također, pripada mjestu gdje bi trebao raditi, zajedno s testom, i nema pločice za uzorkovanje. Test je izražajan, s čovjekom čitljivim imenom i čist očekivati i gdje blok za razbijanje logičkih odjeljaka.

4.4. Kada tablica podataka zakaže

Također vrijedi vidjeti što se događa kada naš test ne uspije:

Uvjet nije zadovoljen: Math.pow (a, b) == c | | | | | 4,0 2 2 | 1 lažno Očekivano: 1 Stvarno: 4,0

Opet nam Spock daje vrlo informativnu poruku o pogrešci. Možemo točno vidjeti koji je red našeg Datatablea uzrokovao neuspjeh i zašto.

5. Ruganje

5.1. Što se ruga?

Ruganje je način promjene ponašanja klase s kojim naša testirana služba surađuje. To je koristan način da možete testirati poslovnu logiku izolirajući svoje ovisnosti.

Klasičan primjer toga bio bi zamjena klase koja upućuje mrežni poziv nečim što se jednostavno pretvara. Za detaljnije objašnjenje vrijedi pročitati ovaj članak.

5.2. Ruganje pomoću Spocka

Spock ima vlastiti podsmiješni okvir, koristeći se zanimljivim konceptima koje je Groovy donio u JVM. Prvo, napravimo instancu a Oponašanje:

PaymentGateway PaymentGateway = Ismijavanje ()

U ovom se slučaju tip našeg lažnog zaključka zaključuje prema varijabli type. Kako je Groovy dinamičan jezik, možemo pružiti i argument tipa, koji nam omogućuje da ne moramo dodijeliti svoj lažni naziv bilo kojoj određenoj vrsti:

def PaymentGateway = Ismijavanje (PaymentGateway)

Sad, kad god zovemo metodu na našem PaymentGateway oponašanje, dat će se zadani odgovor, a da se ne pozove stvarna instanca:

kada: def rezultat = paymentGateway.makePayment (12.99) tada: rezultat == false

Pojam za to je popustljivo ruganje. To znači da će lažne metode koje nisu definirane vratiti razumne zadane vrijednosti, za razliku od izbacivanja iznimke. Ovo je dizajnirano u Spocku, kako bi se napravili podsmjesi i na taj način testovi postaju manje krhki.

5.3. Metoda stubiranja poziva Ismijava

Također možemo konfigurirati metode pozvane na naš lažni odgovor na određeni način na različite argumente. Pokušajmo dobiti naše PaymentGateway podsmijeh za povratak pravi kada izvršimo uplatu od 20:

dato: PaymentGateway.makePayment (20) >> true kada: def rezultat = paymentGateway.makePayment (20) tada: rezultat == true

Ono što je ovdje zanimljivo je kako Spock koristi Groovyjev preopterećenje operatora kako bi ometao pozive metode. S Javom moramo nazvati stvarne metode, što nedvojbeno znači da je rezultirajući kôd opširniji i potencijalno manje izražajan.

Pokušajmo sada s još nekoliko vrsta štucanja.

Kad bismo prestali mariti za argument svoje metode i uvijek se željeli vratiti pravi, mogli bismo koristiti samo podvlaku:

PaymentGateway.makePayment (_) >> true

Ako bismo željeli izmjenjivati ​​različite odgovore, mogli bismo pružiti popis za koji će se svaki element vraćati u slijedu:

PaymentGateway.makePayment (_) >>> [true, true, false, true]

Postoji više mogućnosti, a one bi mogle biti obuhvaćene naprednijim budućim člankom o ruganju.

5.4. Verifikacija

Još jedna stvar koju bismo možda željeli učiniti s podsmijehom je ustvrditi da su na njih pozvane razne metode s očekivanim parametrima. Drugim riječima, trebali bismo provjeriti interakciju s našim ismijavanjima.

Tipičan slučaj upotrebe za provjeru bio bi kada bi metoda na našem lažnom mode imala poništiti povratni tip. U ovom slučaju, budući da nema rezultata na kojem bismo mogli operirati, ne postoji zaključak o ponašanju koje bismo mogli testirati pomoću ispitne metode. Općenito, ako se nešto vrati, tada bi ispitivana metoda mogla na to djelovati, a rezultat te operacije bio bi ono što mi tvrdimo.

Pokušajmo provjeriti zove li se metoda s void tipom povratka:

def "Treba li provjeriti je li obavijest pozvana" () {dato: def notifier = Ismijavanje (Notifier) ​​kada: notifier.notify ('foo') onda: 1 * notifier.notify ('foo')} 

Spock ponovno iskorištava preopterećenje operatera Groovy. Množenjem našeg poziva na način mocks s jednim, govorimo koliko puta očekujemo da je pozvan.

Da naša metoda uopće nije pozvana ili da nije pozvana toliko puta koliko smo odredili, tada nam test ne bi dao informativnu poruku Spockove pogreške. Dokažimo to očekujući da je pozvan dva puta:

2 * notifier.notify ('foo')

Nakon toga, da vidimo kako izgleda poruka o pogrešci. Učinit ćemo to kao i obično; prilično je informativan:

Premalo poziva za: 2 * notifier.notify ('foo') (1 poziv)

Baš poput klizanja, možemo izvršiti i labavije podudaranje provjere. Ako nas nije zanimalo koji je naš parametar metode, mogli bismo upotrijebiti donju crtu:

2 * notifier.notify (_)

Ili ako bismo htjeli biti sigurni da nije pozvan s određenim argumentom, mogli bismo koristiti operator not:

2 * notifier.notify (! 'Foo')

Opet, postoji više mogućnosti, koje bi mogle biti obrađene u budućem naprednijem članku.

6. Zaključak

U ovom smo članku dali kratki presjek kroz testiranje sa Spockom.

Pokazali smo kako, koristeći Groovy, možemo svoje testove učiniti izražajnijima od tipičnog JUnit stoga. Objasnili smo strukturu tehnički podaci i značajke.

Pokazali smo kako je lako izvesti testiranje na temelju podataka, a također i kako su ruganje i tvrdnje jednostavne putem izvorne Spock funkcionalnosti.

Implementacija ovih primjera može se naći na GitHubu. Ovo je projekt zasnovan na Mavenu, pa bi ga trebalo biti lako pokrenuti kakav jest.