writing unit tests with spock framework
Scrittura di unit test con Spock Framework: test fixtures, asserzioni e report
In questo Guida completa per principianti su Spock , un breve Introduzione alla programmazione Spock Framework e Groovy è stato fornito nel nostro precedente tutorial.
In questo tutorial, esamineremo tutti i dettagli / passaggi necessari per iniziare Test di unità a Spock.
Per semplicità, testeremo una semplice applicazione calcolatrice che ha diversi metodi come addizione, sottrazione, moltiplicazione, divisione, ecc., Che accettano tutti parametri interi e restituiscono un output intero.
Cosa imparerai:
- Test unitario con Spock Video Tutorial
- Iniziare
- Parola chiave 'def'
- Il ciclo di vita di una specifica Spock
- Asserzioni di Spock
- Segnalazione
- Conclusione
- Lettura consigliata
Test unitario con Spock Video Tutorial
Iniziare
Simile a qualsiasi altro framework di test unitario, anche Spock può essere utilizzato per scrivere scenari / casi di test per un'applicazione sotto test. Cercheremo di confrontare e confrontare le diverse caratteristiche del framework Spock con i framework esistenti / conosciuti come JUnit .
Parola chiave 'def'
Proviamo prima a capire in breve la parola chiave 'def' di Groovy. La parola chiave def viene utilizzata per definire il tipo-def e può essere utilizzata per dichiarare una funzione e un campo / variabile.
'Def' viene generalmente utilizzato quando non vogliamo limitare il tipo di un campo o restituire il tipo di un metodo. Vediamo alcuni esempi di parola chiave def in una classe groovy e tutti i suoi usi validi.
// def as variable types def inputNum = 100 def inputStr = 'hello world!!' def app = new CalculatorApp() // def as return type of function def 'test function'() { // function body here }
Il ciclo di vita di una specifica Spock
Spock spec quando viene eseguito cerca tutti i test definiti e li esegue uno per uno. Tuttavia, ci sono poche altre funzionalità / caratteristiche fornite da Spock per rendere i test meno ridondanti e più leggibili.
Parliamo di alcune funzionalità di seguito:
Definizione di input / variabili come parte delle specifiche
Considera l'idea di eseguire più test utilizzando tutti gli stessi valori di input. Un modo sarebbe inizializzare i valori di input in ogni test individualmente, altrimenti possiamo definire direttamente i campi a livello di specifica e assicurarci che prima di ogni test, i campi vengano inizializzati e disponibili per il test in esecuzione.
Vediamo un esempio per la nostra classe di applicazione della calcolatrice .
Definiremo i dati di input a livello di specifica in modo che siano disponibili con i valori iniziali a tutti i test presenti nella specifica.
programma di ordinamento rapido in c ++
class CalculatorAppSpec extends Specification { def input1 = 50 def input2 = 10 def result = 0 def app = new CalculatorApp() def 'addition with valid inputs return expected result'() { when: result = app.add(input1, input2) then: result == 60 } def 'multiplication with valid inputs return expected result'() { when: result = app.multiply(input1, input2) then: result == 500 } def 'division with valid inputs return expected result'() { when: result = app.divide(input1, input2) then: result == 5 } def 'subsctraction with valid inputs return expected result'() { when: result = app.substract(input1, input2) then: result == 40 } }
In questo esempio di codice, puoi vedere che abbiamo definito input1, input2, l'applicazione sotto test e il risultato a livello di specifica. Ciò che garantisce è che ogni volta che viene eseguito un test dai file delle specifiche e i campi inizializzati vengono passati a quel test. Questo infatti elimina la necessità di impostare test ogni volta con valori di input.
Dispositivi di prova
Simile alla maggior parte dei framework di unit test, Spock fornisce anche metodi di configurazione e pulizia per eseguire logiche / attività speciali in specifici eventi del ciclo di vita dell'esecuzione del test.
setupSpec & cleanupSpec
Questi metodi vengono chiamati una volta per ogni esecuzione di Spec e vengono chiamati rispettivamente prima e dopo l'esecuzione del test. Questi sono paragonabili a @ Prima della lezione e @ Dopo la lezione annotazioni di JUnit.
configurazione e pulizia
Questi metodi vengono chiamati prima e dopo l'esecuzione di ogni test nelle specifiche.
Questi hook sono il posto giusto per qualsiasi logica / pezzo di codice che desideri eseguire prima e dopo l'esecuzione del test. Per esempio , Nella pulizia, è possibile scrivere un codice per chiudere la connessione al database (se presente) utilizzata durante il test.
Questi possono essere paragonati a @ BeforeTest e @ AfterTest annotazioni in JUnit.
Vediamo un esempio di questi dispositivi nel nostro test dell'applicazione della calcolatrice.
def setupSpec() { println('###in setup spec!') } def cleanupSpec() { println('###in cleanup spec!') } def setup() { println('>>>in test setup!') } def cleanup() { println('>>>in test cleanup!') }
Se il codice del dispositivo di test sopra viene aggiunto a una specifica contenente 4 test, l'output sarà il seguente:
file swf non riprodotti nel browser
###in setup spec! >>>in test setup! >>>in test cleanup! >>>in test setup! >>>in test cleanup! >>>in test setup! >>>in test cleanup! >>>in test setup! >>>in test cleanup! ###in cleanup spec!
Asserzioni di Spock
Le affermazioni in Spock sono chiamate asserzione di potere (ed è stata adottata da Groovy in seguito dopo essere stata introdotta da Spock). Le asserzioni di Spock forniscono molte eccezioni diagnostiche in caso di qualsiasi asserzione fallita.
Si può facilmente scoprire cosa è andato storto osservando semplicemente la diagnostica del fallimento invece che verbosa AssertionErrors in JUnit e altri framework.
Proviamo a capirlo con un esempio e confrontarlo con JUnit
Lavoreremo con un semplice test che verifica l'uguaglianza delle stringhe e vedremo quali dati diagnostici vengono generati in caso di errore di asserzione.
Test di Spock
def 'check case-insensitive equality of 2 strings'() { given: 'two input strings' String str1 = 'hello' String str2 = 'HELLO world' when: 'strings are lowercased' str1 = str1.toLowerCase() str2 = str2.toLowerCase() then: 'equal strings should return success' str1 == str2 }
JUnit Test
@Test public void compareStrings_withValidInput_shouldReturnSuccess() { // Arrange String str1 = 'hello'; String str2 = 'HELLO world'; // Act str1 = str1.toLowerCase(); str2 = str2.toLowerCase(); // Assert Assert.assertEquals(str1,str2); }
Uscita di Spock
Condition not satisfied: str1 == str2 | | | hello| hello world false 6 differences (45% similarity) hello(------) hello( world) Expected :hello world Actual :hello
Uscita JUnit
org.junit.ComparisonFailure: Expected :hello Actual :hello world
Come puoi dedurre da sopra, le informazioni diagnostiche fornite da Spock hanno dettagli migliori ed è più user-friendly rispetto ad altri framework come JUnit.
Suggerimenti e trucchi per le asserzioni
Asserire più elementi contemporaneamente - Spock fornisce varie scorciatoie per le asserzioni e una di queste è la notazione '*' che consente di affermare gli elementi nell'elenco.
Capiamo questo con un esempio:
Considera una classe CityInfo con cityName e popolazione come campi. Scriveremo un test di Spock per affermare i nomi delle città presenti nell'elenco fornito.
public class CityInfo { public CityInfo(String cityName, int population) { this.cityName = cityName; this.population = population; } public String cityName; public int population; }
Vediamo ora il test:
def 'Assert multiple elements of list' () { given: def cityList = new LinkedList() cityList.add(new CityInfo('Mumbai', 120)) cityList.add(new CityInfo('Delhi', 80)) cityList.add(new CityInfo('Chennai', 100)) expect: cityList*.cityName == ['Mumbai', 'Delhi', 'Chennai'] }
Come mostrato nella scorciatoia di asserzione sopra, è possibile convalidare l'intero elenco con l'aiuto della parola chiave '*'.
Vediamo anche come sarebbe stato un fallimento. Rimuoverò il nome di una città dall'affermazione sopra.
Condition not satisfied: cityList*.cityName == ['Delhi', 'Chennai'] | | | | | false | [Mumbai, Delhi, Chennai] [app.CityInfo@31368b99, app.CityInfo@1725dc0f, app.CityInfo@3911c2a7]
Puoi vedere che le informazioni diagnostiche del fallimento di un'asserzione sono ricche e facili da comprendere.
Sfruttando il parametro di chiusura - ogni ().
Vediamo come possiamo sfruttare il parametro di chiusura denominato every () per aggiungere un'asserzione per ogni elemento di un elenco o di una raccolta. Nello stesso esempio, proviamo ad aggiungere un'asserzione che convalidi la popolazione di ogni città se l'input fornito è> 50.
def 'Assert multiple elements of list' () { given: def cityList = new LinkedList() cityList.add(new CityInfo('Mumbai', 120)) cityList.add(new CityInfo('Delhi', 80)) cityList.add(new CityInfo('Chennai', 100)) expect: cityList*.cityName == ['Mumbai', 'Delhi', 'Chennai'] and: cityList.population.every() { it > 50 } }
Affermare le eccezioni generate
È possibile affermare che le eccezioni vengano lanciate nel blocco 'then' (che significa a quando è richiesto anche il blocco). È possibile diagnosticare i dettagli dell'eccezione assegnando l'eccezione generata a un campo e affermando le proprietà richieste dell'eccezione generata.
Usiamo la stessa classe CityInfo e definiamo un metodo che genera un'eccezione e scriviamo un test per essa.
public class CityInfo { public CityInfo(String cityName, int population) { this.cityName = cityName; this.population = population; } public String cityName; public int population; public CityInfo() { } public int getCleanlinessScore() { throw new RuntimeException('method not implemented'); } }
Diamo ora un'occhiata al test:
def 'cleanliness score throws runtime exception with message - method not implemented'() { given: CityInfo app = new CityInfo(); when: app.cleanlinessScore() then: def e = thrown(RuntimeException) e.message == 'method not implemented' }
Segnalazione
Per generare report bellissimi e dettagliati basati su HTML, sono disponibili librerie che possono essere aggiunte nel file di compilazione e ora ogni volta che i test vengono eseguiti durante la compilazione (o tramite esecuzione diretta), verrà generato un report dettagliato basato su html cartella di output.
Per ottenere i rapporti di prova generati, aggiungi le seguenti librerie al file build.gradle (e in modo simile anche al file Maven pom.xml).
testCompile 'com.athaydes:spock-reports:1.6.1' testCompile 'org.slf4j:slf4j-api:1.7.13' testCompile 'org.slf4j:slf4j-simple:1.7.13'
Ora crea il progetto ed esegui i test eseguendo tutti i test nella cartella 'test' o eseguendo ' gradle clean test '.
Puoi aprire index.html file per ottenere un rapporto riepilogativo per tutte le specifiche di Spock disponibili per essere eseguite.
continua a ricevere il gateway predefinito non disponibile
Se vuoi vedere il rapporto dettagliato per una specifica specifica, fai clic sulla specifica dall'elenco sopra e puoi vedere un rapporto dettagliato di errori e successi.
Conclusione
In questo tutorial, abbiamo coperto le basi dello unit test con Spock Framework. Abbiamo visto i diversi modi e abbreviazioni per scrivere asserzioni e il tipo di informazioni diagnostiche complete generate dal framework Spock per gli errori di asserzione.
Abbiamo anche esaminato il modo in cui possiamo generare dei report piuttosto silenziosi basati su HTML per i test di Spock che includono la stessa diagnostica dettagliata per i test eseguiti.
Il nostro prossimo tutorial ti illustrerà in dettaglio come scrivere test parametrizzati con Spock !!
Tutorial PREV | PROSSIMO Tutorial
Lettura consigliata
- Test basato sui dati o parametrizzato con Spock Framework
- Domande dell'intervista a Spock con risposte (le più popolari)
- Spock per integrazione e test funzionali con selenio
- Spock beffardo e stubbing (esempi con tutorial video)
- Tutorial di Spock: test con Spock e Groovy
- Tutorial Mockito: Mockito Framework per Mocking in Unit Testing
- Le differenze tra test unitari, test di integrazione e test funzionali
- La chiave per un test unitario di successo: come gli sviluppatori testano il proprio codice?