Úvod do testovania s programami Spock a Groovy

1. Úvod

V tomto článku sa pozrieme na Spock, testovací rámec Groovy. Hlavným cieľom Spocku je byť výkonnejšou alternatívou k tradičnému zásobníku JUnit využitím funkcií Groovy.

Groovy je jazyk založený na JVM, ktorý sa bezproblémovo integruje s Javou. Okrem interoperability ponúka ďalšie jazykové koncepty, ako napríklad dynamický jazyk, voliteľné typy a metaprogramovanie.

Využitím programu Groovy predstavuje Spock nové a expresívne spôsoby testovania našich aplikácií Java, ktoré v bežnom kóde Java jednoducho nie sú možné. V tomto článku sa pozrieme na niektoré z konceptov Spocka na vysokej úrovni s niekoľkými praktickými príkladmi krok za krokom.

2. Závislosť od Maven

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

 org.spockframework testovacie jadro 1.0-groovy-2.4 test org.codehaus.groovy groovy-all 2.4.7 test 

Pridali sme Spock aj Groovy ako každú štandardnú knižnicu. Pretože je však Groovy nový jazyk JVM, musíme doň zahrnúť gmavenplus plugin, aby bolo možné kompilovať a spustiť ho:

 org.codehaus.gmavenplus gmavenplus-plugin 1.5 kompilovať testCompile 

Teraz sme pripravení napísať náš prvý Spockov test, ktorý bude napísaný v Groovy kóde. Upozorňujeme, že Groovy a Spock používame iba na testovacie účely, a preto sú tieto závislosti obmedzené na rozsah.

3. Štruktúra Spockovho testu

3.1. Špecifikácie a vlastnosti

Keď píšeme naše testy v Groovy, musíme ich pridať do src / test / groovy adresár, namiesto src / test / java. Vytvorme náš prvý test v tomto adresári, ktorý ho pomenujeme Specification.groovy:

trieda FirstSpecification rozširuje špecifikáciu {}

Upozorňujeme, že rozširujeme Špecifikácia rozhranie. Každá trieda Spocku to musí rozšíriť, aby mala k dispozícii rámec. Robí to tak, aby sme mohli implementovať náš prvý vlastnosť:

def "jeden plus jeden by sa mal rovnať dvom" () {očakávať: 1 + 1 == 2}

Pred vysvetlením kódu stojí za zmienku aj to, že v Spocku, čo označujeme ako a vlastnosť je do istej miery synonymom toho, čo vidíme ako test v JUnit. Takže kedykoľvek hovoríme o a vlastnosť vlastne hovoríme o a test.

Poďme si teraz rozobrať naše vlastnosť. Pritom by sme mali okamžite vidieť určité rozdiely medzi programom Java a Java.

Prvý rozdiel je v tom, že názov metódy funkcie je napísaný ako obyčajný reťazec. V JUnit by sme mali názov metódy, ktorý na oddelenie slov používa camelcase alebo podčiarknutie, čo by nebolo také expresívne ani ľudsky čitateľné.

Ďalším je, že náš testovací kód žije v očakávať blokovať. Blokom sa budeme podrobnejšie venovať v najbližšej dobe, ale v podstate sú to logické spôsoby rozdelenia jednotlivých krokov našich testov.

Nakoniec si uvedomujeme, že neexistujú žiadne tvrdenia. Je to preto, lebo tvrdenie je implicitné a pominie, keď sa naše tvrdenie rovná pravda a zlyháva, keď sa rovná nepravdivé. Opäť sa budeme čoskoro podrobnejšie zaoberať tvrdeniami.

3.2. Bloky

Niekedy si pri písaní testu JUnit môžeme všimnúť, že neexistuje expresívny spôsob rozdelenia na časti. Napríklad, ak by sme sledovali vývoj zameraný na správanie, mohli by sme skončiť označením daný vtedy diely pomocou komentárov:

@Test public void givenTwoAndTwo_whenAdding_thenResultIsFour () {// Najprv zadané int = 2; int sekunda = 4; // When int result = 2 + 2; // Potom assertTrue (výsledok == 4)}

Spock tento problém rieši blokmi. Bloky sú Spockovým natívnym spôsobom rozdelenia fáz nášho testu pomocou štítkov. Dávajú nám štítky na daný vtedy a viac:

  1. Nastaviť (Aliased by Given) - Tu vykonáme všetky potrebné nastavenia pred spustením testu. Toto je implicitný blok, ktorého súčasťou sa vôbec nestane kód v žiadnom bloku
  2. Kedy - Tu poskytujeme a stimul na to, čo je v teste. Inými slovami, kde zvoláme našu testovanú metódu
  3. Potom - Sem patria tvrdenia. V Spocku sa tieto hodnotia ako obyčajné boolovské tvrdenia, ktorým sa budeme venovať neskôr
  4. Očakávať - Toto je spôsob vykonávania našich stimul a tvrdenie v rovnakom bloku. Podľa toho, čo považujeme za výraznejšie, sa tento blok môžeme alebo nemusíme rozhodnúť použiť
  5. Vyčistiť - Tu strhneme všetky zdroje závislé od testu, ktoré by inak zostali pozadu. Napríklad by sme mohli chcieť odstrániť všetky súbory zo súborového systému alebo odstrániť testovacie údaje zapísané do databázy

Skúsme náš test implementovať znova, tentokrát však naplno využime bloky:

def "dva plus dva by sa mali rovnať štyrom" () {dané: int vľavo = 2 int vpravo = 2 keď: int výsledok = vľavo + vpravo potom: výsledok == 4}

Ako vidíme, bloky uľahčujú čitateľnosť nášho testu.

3.3. Využitie Groovy funkcií pre tvrdenia

V rámci potom a očakávať bloky, tvrdenia sú implicitné.

Väčšinou sa každý výrok vyhodnotí a potom zlyhá, ak nie pravda. Keď to spojíte s rôznymi funkciami Groovy, urobí dobrú prácu, keď odstráni potrebu knižnice tvrdení. Skúsme a zoznam preukázať toto:

def "Malo by byť možné odstrániť zo zoznamu" () {given: def list = [1, 2, 3, 4] when: list.remove (0) then: list == [2, 3, 4]}

Aj keď sa v tomto článku iba krátko dotkneme Groovy, stojí za to vysvetliť, čo sa tu deje.

Po prvé, Groovy nám poskytuje jednoduchšie spôsoby vytvárania zoznamov. Môžeme len vedieť deklarovať svoje prvky v hranatých zátvorkách a interne a zoznam bude inštancovaný.

Po druhé, pretože Groovy je dynamický, môžeme ho použiť def čo znamená, že pre svoje premenné nedeklarujeme typ.

A nakoniec, v kontexte zjednodušenia nášho testu je najužitočnejšou preukázanou funkciou preťaženie operátora. To znamená, že interne, namiesto referenčného porovnania ako v Jave, rovná sa () bude použitá metóda na porovnanie týchto dvoch zoznamov.

Tiež stojí za to demonštrovať, čo sa stane, keď náš test zlyhá. Poďme to rozbiť a potom si pozrite, čo je výstup na konzolu:

Podmienka nie je splnená: list == [1, 3, 4] | | | false [2, 3, 4] pri FirstSpecification.Should be remove from list (FirstSpecification.groovy: 30)

Zatiaľ čo všetko, čo sa deje, volá rovná sa () na dvoch zoznamoch je Spock dosť inteligentný na to, aby vykonal rozpis zlyhávajúceho tvrdenia, čo nám dáva užitočné informácie na ladenie.

3.4. Uplatňovanie výnimiek

Spock nám tiež poskytuje expresívny spôsob kontroly výnimiek. V JUnit možno niektoré naše možnosti používajú a Skús chytiť blokovať, deklarovať očakávané v hornej časti nášho testu alebo s využitím knižnice tretej strany. Spockove natívne tvrdenia prichádzajú so spôsobom riešenia výnimiek po vybalení z krabice:

def "Mal by byť index mimo hranice pri odstraňovaní neexistujúcej položky" () {given: def list = [1, 2, 3, 4] when: list.remove (20) then: thrown (IndexOutOfBoundsException) list. veľkosť () == 4}

Tu sme nemuseli predstavovať ďalšiu knižnicu. Ďalšou výhodou je, že vyhodený () metóda potvrdí typ výnimky, ale nezastaví vykonanie testu.

4. Testovanie na základe údajov

4.1. Čo je testovanie na základe dát?

V podstate testovanie na základe dát je, keď testujeme rovnaké správanie niekoľkokrát s rôznymi parametrami a tvrdeniami. Klasickým príkladom by to mohlo byť testovanie matematickej operácie, ako napríklad druhé mocniny čísla. V závislosti od rôznych permutácií operandov bude výsledok iný. V Jave je pojmom, ktorý môžeme lepšie poznať, parametrické testovanie.

4.2. Implementácia parametrizovaného testu v Jave

V niektorých súvislostiach stojí za to implementovať parametrizovaný test pomocou JUnit:

@RunWith (Parameterized.class) verejná trieda FibonacciTest {@Parameters public static Collection data () {return Arrays.asList (new Object [] [] {{1, 1}, {2, 4}, {3, 9}} ); } vstup súkromného int; súkromný int sa očakáva; public FibonacciTest (int vstup, int ocakavany) {this.input = vstup; this.exected = expect; } @Test public void test () {assertEquals (fExpected, Math.pow (3, 2)); }}

Ako vidíme, je tu pomerne veľa výrečnosti a kód nie je príliš dobre čitateľný. Museli sme vytvoriť dvojrozmerné pole objektov, ktoré žije mimo test, a dokonca aj obalový objekt na vloženie rôznych testovacích hodnôt.

4.3. Používanie dátových súborov v aplikácii Spock

Jednou ľahkou výhrou pre Spock v porovnaní s JUnitom je to, ako čisto implementuje parametrizované testy. V prípade Spocka je to opäť známe ako Testovanie na základe údajov. Teraz teda poďme znovu implementovať ten istý test, tentokrát však použijeme Spock Údajové tabuľky, ktorý poskytuje oveľa pohodlnejší spôsob vykonávania parametrizovaného testu:

def "čísla na mocninu dvoch" (int a, int b, int c) 4 3 

Ako vidíme, máme k dispozícii priamu a expresnú tabuľku údajov, ktorá obsahuje všetky naše parametre.

Okrem testu tiež patrí tam, kam má robiť, a nie je tu žiadny štandardný štítok. Test je expresívny, má zrozumiteľný názov a je čistý očakávať a kde blok na rozdelenie logických častí.

4.4. Keď zlyhá dátový údaj

Je tiež potrebné vidieť, čo sa stane, keď náš test zlyhá:

Podmienka nie je splnená: Math.pow (a, b) == c | | | | | 4,0 2 2 | 1 nepravdivý Očakávaný: 1 Skutočný: 4.0

Spock nám opäť dáva veľmi poučné chybové hlásenie. Vidíme presne, ktorý riadok našej Datatable spôsobil zlyhanie a prečo.

5. Vysmievanie sa

5.1. Čo je to posmech?

Mocking je spôsob, ako zmeniť správanie triedy, s ktorou spolupracuje naša testovaná služba. Je to užitočný spôsob, ako testovať obchodnú logiku izolovane od jej závislostí.

Klasickým príkladom toho je nahradenie triedy, ktorá robí sieťové volanie, niečím, čo to jednoducho predstiera. Pre hlbšie vysvetlenie stojí za prečítanie tohto článku.

5.2. Mocking pomocou Spocka

Spock má svoj vlastný zosmiešňovací rámec, ktorý využíva zaujímavé koncepty, ktoré na JVM priniesol Groovy. Najskôr vytvoríme inštanciu a Mock:

PaymentGateway PaymentGateway = Mock ()

V takom prípade je typ našej simulácie odvodený od typu premennej. Pretože Groovy je dynamický jazyk, môžeme poskytnúť aj argument typu, ktorý nám umožní, aby sme nemuseli priraďovať náš falošný výraz k konkrétnemu typu:

def PaymentGateway = Mock (PaymentGateway)

Teraz, kedykoľvek zavoláme metódu na našom Platobná brána predstierať, bude poskytnutá predvolená odpoveď bez vyvolania skutočnej inštancie:

kedy: def result = PaymentGateway.makePayment (12,99) potom: result == false

Termín pre toto je mierny výsmech. To znamená, že falošné metódy, ktoré neboli definované, vrátia rozumné predvolené hodnoty, na rozdiel od zavedenia výnimky. Toto je zámerné v aplikácii Spock, aby boli falošné, a tým pádom menej krehké.

5.3. Vyžaduje sa metóda Stubbing Vysmievanie

Môžeme tiež nakonfigurovať metódy volané na našu falošnú reakciu určitým spôsobom na rôzne argumenty. Skúsme si zohnať našu Platobná brána predstieraný návrat pravda keď uskutočňujeme platbu vo výške 20:

zadané: paymentGateway.makePayment (20) >> true keď: def result = PaymentGateway.makePayment (20) potom: result == true

Zaujímavé tu je, ako Spock využíva preťaženie operátora Groovy na potlačenie volaní metód. V prípade Java musíme nazývať skutočné metódy, čo pravdepodobne znamená, že výsledný kód je podrobnejší a potenciálne menej expresívny.

Teraz skúsme niekoľko ďalších typov stubu.

Keby nás prestal zaujímať argument o našej metóde a vždy by sme sa chceli vrátiť pravda, mohli by sme použiť iba podčiarknutie:

PaymentGateway.makePayment (_) >> pravda

Ak by sme chceli striedať rôzne odpovede, mohli by sme poskytnúť zoznam, pre ktorý sa postupne vráti každý prvok:

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

Možností je viac a týmto sa môže venovať pokročilejší budúci článok o posmeškoch.

5.4. Overenie

Ďalšou vecou, ​​ktorú by sme mohli chcieť urobiť s falošnými správami, je tvrdenie, že boli na ne vyvolané rôzne metódy s očakávanými parametrami. Inými slovami, mali by sme overovať interakcie s našimi posmeškami.

Typickým prípadom použitia na overenie by bolo, keby metóda na našom simulátore mala a neplatný návratový typ. V takom prípade, keď nebudeme mať žiadny výsledok, aby sme mohli operovať, neexistuje žiadne odvodené správanie, ktoré by sme mohli testovať pomocou testovanej metódy. Spravidla platí, že ak by sa niečo vrátilo, mohla by to fungovať testovaná metóda a je to výsledok tejto operácie, aký tvrdíme.

Skúsme overiť, či sa volá metóda s neplatným návratovým typom:

def "Malo by sa volať volanie oznámenia" () {given: def notifier = Mock (Notifier) ​​when: notifier.notify ('foo') then: 1 * notifier.notify ('foo')} 

Spock opäť využíva preťaženie operátora Groovy. Vynásobením nášho simulovaného volania metódou jedným hovoríme, koľkokrát očakávame, že bol volaný.

Keby naša metóda nebola volaná vôbec alebo by nebola volaná toľkokrát, koľko sme zadali, potom by náš test nedokázal poskytnúť informatívnu chybovú správu Spocka. Dokážme to tým, že očakávame, že to bolo volané dvakrát:

2 * notifier.notify ('foo')

Potom sa pozrime, ako vyzerá chybové hlásenie. Budeme to ako obvykle; je to dosť informatívne:

Príliš málo vyvolávaní pre: 2 * notifier.notify ('foo') (1 vyvolanie)

Rovnako ako stubbing môžeme vykonať aj voľnejšiu verifikačnú zhodu. Ak by nás nezaujímalo, aký je náš parameter metódy, mohli by sme použiť podčiarknutie:

2 * notifier.notify (_)

Alebo ak by sme sa chceli ubezpečiť, že nebol volaný s konkrétnym argumentom, mohli by sme použiť operátor not:

2 * notifier.notify (! 'Foo')

Opäť existuje viac možností, ktorým sa môže venovať budúci pokročilejší článok.

6. Záver

V tomto článku sme sa podrobne venovali testovaniu pomocou Spocku.

Predviedli sme, ako využitím Groovy môžeme dosiahnuť, aby boli naše testy expresívnejšie ako typický zásobník JUnit. Vysvetlili sme štruktúru technické údaje a Vlastnosti.

A ukázali sme, aké ľahké je vykonať testovanie na základe dát, a tiež to, ako ľahké sú posmešky a tvrdenia pomocou natívnej funkčnosti Spocku.

Implementáciu týchto príkladov možno nájsť na GitHub. Toto je projekt založený na Maven, takže by mal byť ľahko spustiteľný tak, ako je.


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