Bevezetés a Spock és Groovy teszteléséhez

1. Bemutatkozás

Ebben a cikkben egy pillantást vetünk a Spockra, egy Groovy tesztelési keretrendszerre. Főként a Spock arra törekszik, hogy a Groovy funkcióinak kihasználásával erősebb alternatívája legyen a hagyományos JUnit-veremnek.

A Groovy egy JVM-alapú nyelv, amely zökkenőmentesen integrálódik a Java-val. Az interoperabilitás mellett további nyelvi fogalmakat kínál, például dinamikusnak lenni, opcionális típusokkal és meta-programozással.

A Groovy használatával a Spock új és kifejező módszereket vezet be Java-alkalmazásaink tesztelésére, amelyek a hagyományos Java-kódokban egyszerűen nem lehetségesek. A Spock néhány magas szintű fogalmát a cikk során tanulmányozzuk, néhány gyakorlati példával.

2. Maven-függőség

Mielőtt belekezdenénk, adjuk hozzá a Maven-függőségeinket:

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

Hozzáadtuk Spockot és Groovyt is, mint bármelyik szokásos könyvtárat. Mivel azonban a Groovy egy új JVM nyelv, bele kell foglalnunk a gmavenplus beépülő modul a fordításhoz és futtatáshoz:

 org.codehaus.gmavenplus gmavenplus-plugin 1.5 compile testCompile 

Most készen állunk az első Spock-teszt megírására, amelyet Groovy-kóddal írunk. Ne feledje, hogy a Groovy-t és a Spockot csak tesztelési célokra használjuk, ezért tesztelik a függőségeket.

3. A Spock teszt felépítése

3.1. Műszaki adatok és jellemzők

Amint a Groovy-ban írjuk tesztjeinket, hozzá kell adnunk őket a src / test / groovy könyvtár helyett src / test / java. Készítsük el az első tesztünket ebben a könyvtárban, és nevezzük meg Specifikáció.groovy:

A FirstSpecification osztály kiterjeszti a specifikációt {}

Ne feledje, hogy kiterjesztjük a Leírás felület. Minden Spock osztálynak ki kell terjesztenie ezt annak érdekében, hogy elérhetővé tegye a keretrendszert számára. Így teszi lehetővé az első megvalósítását funkció:

def "egy plusz egy egyenlő kettővel" () {várható: 1 + 1 == 2}

Mielőtt elmagyaráznánk a kódot, azt is érdemes megjegyezni, hogy a Spock-ban, amit a-nak nevezünk funkció némileg szinonimája annak, amit a teszt a JUnitben. Így valahányszor a funkció valójában a teszt.

Most elemezzük funkció. Ennek során azonnal képesnek kell lennünk látni néhány különbséget a Java és a Java között.

Az első különbség az, hogy a feature metódus nevét közönséges karakterláncként írják. A JUnit-ben olyan módszerünk lett volna, amely teve vagy aláhúzással használja a szavak elválasztását, ami nem lett volna olyan kifejező vagy emberileg olvasható.

A következő az, hogy a tesztkódunk egy elvárják Blokk. Rövidesen részletesebben áttekintjük a blokkokat, de lényegében logikus módszer a tesztjeink különböző lépéseinek felosztására.

Végül rájövünk, hogy nincsenek állítások. Ez azért van, mert az állítás implicit, akkor múlik el, amikor állításunk megegyezik igaz és kudarcot vall, ha egyenlő hamis. Ismét rövidesen részletesebben kitérünk az állításokra.

3.2. Blokkok

Néha a JUnit teszt írásakor észrevehetjük, hogy nincs kifejezett módja annak, hogy részekre bontsa. Például, ha viselkedésvezérelt fejlődést követtünk, akkor végül a adott mikor alkatrészek megjegyzéseket használva:

@Test public void givenTwoAndTwo_whenAdding_thenResultIsFour () {// Adott int először = 2; int második = 4; // Amikor int eredmény = 2 + 2; // Ezután assertTrue (eredmény == 4)}

Spock blokkokkal oldja meg ezt a problémát. A blokkok a Spock natív módja a teszt fázisainak felosztására címkék segítségével. Adnak nekünk címkéket adott mikor és több:

  1. Beállít (Aliased by Given) - Itt elvégezzük a teszt futtatása előtt szükséges beállításokat. Ez egy implicit blokk, amelynek kódja egy blokkban sem válik annak részévé
  2. Mikor - Itt nyújtjuk a inger arra, ami tesztelés alatt áll. Más szavakkal, ahol a tesztelt módszerünket alkalmazzuk
  3. Azután - Ide tartoznak az állítások. A Spock-ban ezeket egyszerű logikai állításként értékelik, amelyekre később kitérünk
  4. Várja - Ez a módszer a mi előadásunkra inger és állítás ugyanazon a blokkon belül. Attól függően, hogy mit találunk kifejezőbbnek, dönthetünk a blokk használata mellett, vagy sem
  5. Takarítás - Itt lebontjuk a tesztfüggőségi erőforrásokat, amelyek egyébként elmaradnának. Előfordulhat, hogy eltávolítunk minden fájlt a fájlrendszerből, vagy eltávolítjuk az adatbázisba írt tesztadatokat

Próbáljuk meg újra végrehajtani tesztünket, ezúttal teljes mértékben felhasználva a blokkokat:

def "kettő plusz kettőnek négynek kell lennie" () {adott: int bal = 2 int jobb = 2, amikor: int eredmény = bal + jobb, akkor: eredmény == 4}

Mint láthatjuk, a blokkok segítenek tesztünk olvashatóbbá válásában.

3.3. A Groovy-szolgáltatások kihasználása az állításokhoz

Belül azután és elvárják blokkok, az állítások implicitek.

Többnyire minden állítást kiértékelnek, majd elbuknak, ha nem igaz. Amikor ezt különféle Groovy-funkciókkal kapcsolja össze, jó munkát végez, ha feleslegessé teszi egy állítási könyvtár szükségességét. Próbáljuk ki a lista állítás ennek bizonyítására:

def "Képesnek kell lennie a listáról való eltávolításra" () {megadott: def list = [1, 2, 3, 4], amikor: list.remove (0), majd: list == [2, 3, 4]}

Bár ebben a cikkben csak röviden érintjük Groovyt, érdemes elmagyarázni, mi történik itt.

Először is, Groovy egyszerűbb módszereket ad a listák létrehozására. Csak szögletes zárójelekkel deklarálhatjuk elemeinket, és belsőleg a lista példányos lesz.

Másodszor, mivel Groovy dinamikus, használhatjuk def ami csak azt jelenti, hogy nem deklaráljuk a változók típusát.

Végül a tesztünk egyszerűsítése kapcsán a leghasznosabb tulajdonság a kezelő túlterhelése. Ez azt jelenti, hogy belsőleg, ahelyett, hogy a Java-hoz hasonló referencia-összehasonlítást végezne, a egyenlő () metódust hívjuk meg a két lista összehasonlítására.

Érdemes bemutatni azt is, hogy mi történik, ha tesztünk sikertelen. Tegyük tönkre, majd nézzük meg, mi származik a konzolból:

A feltétel nem teljesül: lista == [1, 3, 4] | | | hamis [2, 3, 4] a FirstSpecification-nél. Képesnek kell lennie a listáról való eltávolításra (FirstSpecification.groovy: 30)

Miközben csak a telefonálás folyik egyenlő () két listán a Spock elég intelligens ahhoz, hogy lebontsa a sikertelen állítást, hasznos információkkal szolgálva a hibakereséshez.

3.4. Kivételek érvényesítése

Spock emellett kifejező módon is ellenőrizheti a kivételeket. A JUnitben egyes lehetőségeink a próbáld elkapni letiltani, kijelenteni várt tesztünk tetején, vagy egy harmadik fél könyvtárának felhasználásával. Spock őshonos állításainak módja a kivételek kezelése a dobozon kívül:

def "Ha nem létező elem eltávolításakor indexet kellene kapni a határoktól" () {megadott: def list = [1, 2, 3, 4], amikor: list.remove (20), majd: thrown (IndexOutOfBoundsException) lista. méret () == 4}

Itt nem kellett további könyvtárat bevezetnünk. További előny, hogy a dobott() A módszer a kivétel típusát érvényesíti, de nem állítja le a teszt végrehajtását.

4. Adatközpontú tesztelés

4.1. Mi az adatközpontú tesztelés?

Lényegében, adatvezérelt tesztelés az, amikor ugyanazt a viselkedést többször teszteljük, különböző paraméterekkel és állításokkal. Klasszikus példa erre egy matematikai művelet tesztelése, például egy szám négyzetesítése. Az operandusok különböző permutációitól függően az eredmény más lesz. A Java-ban a kifejezés, amelyet jobban ismerhetünk, a paraméterezett tesztelés.

4.2. Paraméteres teszt megvalósítása Java-ban

Bizonyos körülmények között érdemes egy paraméterezett tesztet végrehajtani a JUnit használatával:

@RunWith (Parameterized.class) public class FibonacciTest {@Parameters public static Collection data () {return Arrays.asList (new Object [] [] {{1, 1}, {2, 4}, {3, 9}} ); } privát int bemenet; magán int várható; public FibonacciTest (int bemenet, int várható) {this.input = input; this. várható = várható; } @Test public void test () {assertEquals (fExpected, Math.pow (3, 2)); }}

Amint láthatjuk, meglehetősen sok a szóhasználat, és a kód nem túl olvasható. Létre kellett hoznunk egy kétdimenziós objektumtömböt, amely a teszten kívül él, és még egy burkolóobjektumot is a különböző tesztértékek beadására.

4.3. Adattáblák használata a Spockban

A Spock számára a JUnit-hez képest az egyik egyszerű győzelem, hogy tisztán végrehajtja a paraméterezett teszteket. Ismét Spockban ez az úgynevezett Adatvezérelt tesztelés. Most hajtsuk végre ugyanazt a tesztet, csak ezúttal a Spockot fogjuk használni Adattáblák, amely sokkal kényelmesebb módot kínál a paraméterezett teszt végrehajtására:

def "számok kettő erejéig" (int a, int b, int c) 4 3 

Mint láthatjuk, csak egy egyszerű és kifejező Adattáblánk van, amely tartalmazza az összes paraméterünket.

Továbbá, a teszt mellé tartozik, ahol meg kell tennie, és nincs kazán. A teszt kifejező, emberileg olvasható névvel és tiszta elvárják és hol blokk a logikai szakaszok lebontásához.

4.4. Ha egy adatbázissal nem sikerül

Érdemes megnézni, mi történik, ha tesztünk sikertelen:

A feltétel nem teljesül: Math.pow (a, b) == c | | | | | 4,0 2 2 | 1 hamis Várható: 1 tényleges: 4,0

A Spock ismét nagyon informatív hibaüzenetet küld nekünk. Pontosan láthatjuk, hogy az Adatbázisunk melyik sora okozott hibát és miért.

5. Gúnyolódás

5.1. Mi a gúnyolódás?

A gúnyolás az osztály viselkedésének megváltoztatásának módja, amellyel tesztelt szolgáltatásunk együttműködik. Hasznos módszer arra, hogy az üzleti logikát a függőségektől elkülönítve tesztelhessük.

Klasszikus példa erre egy olyan osztály cseréje, amelyik hálózati hívást kezdeményez, olyannal, ami egyszerűen csak úgy tesz, mintha. Részletesebb magyarázatért érdemes elolvasni ezt a cikket.

5.2. Gúny a Spock segítségével

Spock saját gúnyos keretrendszerrel rendelkezik, érdekes fogalmakat felhasználva Groovy által a JVM-hez. Először példázzuk a Gúny:

PaymentGateway paymentGateway = Gúny ()

Ebben az esetben az álunk típusára a változó típus következtet. Mivel a Groovy egy dinamikus nyelv, megadhatunk egy típusú argumentumot is, lehetővé téve számunkra, hogy ne kelljen hozzárendelnünk egy bizonyos típusú típust:

def paymentGateway = Gúny (PaymentGateway)

Most, amikor hívunk egy módszert a miénkre PaymentGateway gúnyolódni, alapértelmezett választ adunk, anélkül, hogy valós példányt hívnánk meg:

mikor: def eredmény = paymentGateway.makePayment (12.99), akkor: eredmény == hamis

Ennek kifejezése az engedékeny gúnyolódás. Ez azt jelenti, hogy a megmunkálatlan metódusok ésszerű alapértelmezéseket adnak vissza, szemben a kivétel vetésével. Ez Spock-ban tervezett módon, annak érdekében, hogy a gúnyokat és a teszteket kevésbé törékeny legyen.

5.3. Szúrási módszer felhívja Gúnyok

Konfigurálhatjuk azokat a módszereket is, amelyekre az álunkra felhívják, hogy bizonyos módon reagáljanak a különböző érvekre. Próbáljuk megszerezni a magunkét PaymentGateway gúny, hogy visszatérjen igaz amikor befizetünk 20:

adott: paymentGateway.makePayment (20) >> igaz, amikor: def eredmény = paymentGateway.makePayment (20), akkor: eredmény == igaz

Ami itt érdekes, hogy a Spock hogyan használja ki Groovy operátorának túlterhelését a metódushívások elhallgatása érdekében. A Java-val valódi metódusokat kell meghívnunk, ami vitathatatlanul azt jelenti, hogy a kapott kód bőbeszédűbb és potenciálisan kevésbé kifejező.

Most próbálkozzunk még néhány tönkreteszéssel.

Ha abbahagynánk a módszer érvelésünket és mindig visszatérnénk igaz, csak aláhúzást használhatnánk:

paymentGateway.makePayment (_) >> igaz

Ha váltogatni szeretnénk a különböző válaszokat, megadhatnánk egy listát, amelyhez az egyes elemek sorrendben kerülnek visszaadásra:

paymentGateway.makePayment (_) >>> [igaz, igaz, hamis, igaz]

Több lehetőség kínálkozik, és ezekre egy későbbi fejlettebb cikk is kitérhet.

5.4. Igazolás

Egy másik dolog, amit érdemes lehet megcsinálnunk a gúnyokkal, az az állítás, hogy különféle módszereket hívtak fel rájuk elvárt paraméterekkel. Más szavakkal, ellenőriznünk kell a gúnyainkkal való kölcsönhatást.

Az igazolás tipikus felhasználási esete az lenne, ha az álunkon alkalmazott módszer a üres visszatérési típus. Ebben az esetben, mivel nincs eredmény operálásra, nincs következtetett magatartás, amelyet tesztelhetnénk a tesztelt módszerrel. Általában, ha valamit visszaküldtek, akkor a tesztelt módszer működhet rajta, és ennek a műveletnek az eredménye az, amit állítunk.

Próbáljuk meg ellenőrizni, hogy egy void return típusú metódust hívnak-e:

def "Ha ellenőrizni kell az értesítést hívták" () {megadott: def notifier = Mock (Notifier) ​​amikor: notifier.notify ('foo'), akkor: 1 * notifier.notify ('foo')} 

Spock ismét kihasználja a Groovy operátor túlterhelését. Ha megsokszorozzuk a gócmódszeres hívásunkat eggyel, azt mondjuk, hogy hányszor várjuk, hogy hívják.

Ha a módszerünket egyáltalán nem hívták volna meg, vagy alternatív módon nem hívták volna meg annyiszor, amennyit megadtunk, akkor tesztünk nem adott volna informatív Spock hibaüzenetet. Bizonyítsuk be, hogy azt várjuk, hogy kétszer hívták:

2 * notifier.notify ('foo')

Ezt követően nézzük meg, hogyan néz ki a hibaüzenet. Megtesszük, mint általában; elég informatív:

Túl kevés invokáció a következőhöz: 2 * notifier.notify ('foo') (1 meghívás)

A csöppséghez hasonlóan lazább ellenőrzési egyeztetést is végezhetünk. Ha nem érdekelne, mi a módszer paraméterünk, aláhúzást használhatunk:

2 * notifier.notify (_)

Vagy ha azt szeretnénk, hogy megbizonyosodjunk arról, hogy nem egy adott érvvel hívják meg, használhatjuk a not operátort:

2 * notifier.notify (! 'Foo')

Ismét több lehetőség kínálkozik, amelyekre egy későbbi, fejlettebb cikk foglalkozhat.

6. Következtetés

Ebben a cikkben egy gyors szeletet adtunk a Spock tesztelésével.

Bemutattuk, hogy a Groovy kihasználásával hogyan tehetjük a teszteket kifejezőbbé, mint a tipikus JUnit-verem. Elmagyaráztuk a szerkezetét specifikációk és jellemzők.

Megmutattuk, hogy mennyire egyszerű elvégezni az adatközpontú tesztelést, valamint azt is, hogy a natív Spock-funkciók révén milyen könnyű a gúnyolódás és az állítások elkészítése.

Ezeknek a példáknak a megvalósítása megtalálható a GitHub oldalon. Ez egy Maven-alapú projekt, ezért könnyen futtathatónak kell lennie.