spock mocking stubbing
Schernire, soffocare e spiare con Spock:
Test con parametri in Spock Framework è stato spiegato in dettaglio in questo Serie di tutorial di formazione su Spock .
Mocking e Stubbing sono uno degli elementi costitutivi più essenziali di test unitari estesi. Il supporto per beffe e sottotitoli è come la ciliegina sulla torta per un framework.
Per i framework esistenti come JUnit, JBehave, ecc. Il supporto per mock e stub non viene fornito immediatamente, quindi richiede che uno sviluppatore utilizzi librerie di terze parti come Mockito, PowerMock, EasyMock, ecc. Per poterle utilizzare nel unit test.
Per comprendere mock e stub e i loro casi d'uso, puoi dare un'occhiata alla nostra serie di Tutorial Mockito .
domande di intervista allo sviluppatore salesforce per esperti
In questo tutorial, impareremo di più sulle funzionalità Mocking e Stubbing integrate nella stessa libreria di Spock che a sua volta consentirebbe di utilizzare la sintassi Groovy più semplice e quindi riduce la necessità di aggiungere / includere qualsiasi altra 3rdbiblioteche di partito.
Puoi sempre includere altri framework Mocking nei tuoi test, poiché anche tutto il codice Java valido è codice Groovy valido.
Cosa imparerai:
- Applicazione in prova
- Schernendo a Spock
- Stubbing a Spock
- Spiare a Spock
- Conclusione
- Codice sorgente per l'applicazione
- Lettura consigliata
Applicazione in prova
Definiamo prima un'applicazione Java di esempio, che testeremo utilizzando mock e stub nel framework Spock.
Lavoreremo su un'app StudentGradeCalculator che prende il punteggio totale da un database astratto per un dato ID studente e ha una semplice logica di assegnazione dei voti a seconda del valore del punteggio totale. Useremo un'interfaccia database che ha pochi metodi per recuperare e aggiornare i punteggi e i voti degli studenti.
Il codice per l'applicazione sarà disponibile nell'ultima sezione di questo tutorial.
Schernendo a Spock
Video tutorial
In questa sezione, vedremo come istanziare e inizializzare i mock nel framework Spock e come convalidare le interazioni sul mock, ovvero la convalida delle chiamate ai mock è avvenuta secondo le aspettative del metodo sotto test.
Con Mocks, non devi fare molte configurazioni, ma puoi convalidare le interazioni avvenute con gli oggetti fittizi forniti all'applicazione sotto test.
Con i mock, puoi fare cose come:
- Con quali argomenti sono stati chiamati gli scherzi?
- Qual è stato il numero totale di invocazioni, ecc.?
- Accertamento dell'ordine dei mock.
Vediamo un semplice esempio di StudentGradeCalculator, in cui forniamo l'oggetto di implementazione del database fittizio e convalidiamo le interazioni con Mock. Cercheremo di comprendere le caratteristiche beffarde con semplici esempi.
Si noti che tutte le convalide delle interazioni dovrebbero avvenire nel blocco 'then' per convenzione.
Di seguito è riportato il codice per il metodo in prova (che sarà chiamato nel ' quando: 'Blocco)
public String calculateStudentGrade(String studentId) { String grade; // check if grade is already there in database grade = studentDatabase.getStudentGrade(studentId); if(grade!=null && !grade.isEmpty()) { return grade; } List scoreList = studentDatabase.getStudentScores(studentId); Float totalScore = 0F; if(scoreList !=null) totalScore = scoreList.stream().reduce(0F,(a,b)->a+b); if(totalScore > 90) { grade = 'A'; } else if (totalScore > 80) { grade = 'B'; } else { grade = 'C'; } // update the calculated grade in database studentDatabase.updateStudentGrade(studentId, grade); return grade; }
# 1) Convalida delle interazioni con argomenti esatti: convalidiamo prima le interazioni con gli argomenti esattamente previsti. Qui ci aspettiamo che i metodi derisi vengano chiamati con gli argomenti esatti (secondo il flusso di esecuzione del metodo).
Qui ' studentDatabase 'È il mock of a database interface per il quale stiamo convalidando le interazioni.
def 'illustrate mocks for interaction verification with arguments'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade('123','C') 1*studentDatabase.getStudentGrade('123') }
Come mostrato sopra, stiamo convalidando con gli argomenti esatti, in modo che l'implementazione derisa debba essere stata chiamata con. Qualsiasi modifica a questi argomenti causerà il fallimento del test e il registro degli errori mostra il motivo appropriato.
Proviamo a cambiare il voto in ' updateStudentGrade 'Su' A 'invece di' C 'e vedere quale errore otteniamo quando il test viene eseguito.
Too few invocations for: 1*studentDatabase.updateStudentGrade('123','A') (0 invocations) Unmatched invocations (ordered by similarity): 1 * studentDatabase.updateStudentGrade('123', 'C') 1 * studentDatabase.getStudentScores('123')
Mostrerà un errore come 'Troppe poche chiamate' in quanto non riesce a trovare la chiamata Mock con gli argomenti forniti.
domande e risposte dell'intervista Android per 3 anni di esperienza
#Due) Ora vediamo come convalidare le interazioni Mock senza fornire i valori effettivi dell'argomento, ovvero ciò a cui siamo interessati è solo sapere che il mock è stato invocato sul metodo ma non con quali argomenti.
Questi tipi di requisiti sono più comuni durante la scrittura di unit test per il codice di produzione effettivo in quanto non è sempre facile identificare gli argomenti effettivi che dipendono essenzialmente dalla logica aziendale principale dell'applicazione sottoposta a test.
La sintassi è semplice, è sufficiente utilizzare un trattino basso '_' per un argomento di cui non si conosce il valore effettivo.
Per esempio, per verificare la presenza di un valore String, puoi semplicemente menzionare '_ Come String 'Al posto di un argomento nel test e dovrebbe passare per qualsiasi valore String (in modo simile per altri tipi di dati primitivi e personalizzati).
Capiamo questo con un esempio
def 'illustrate mocks for interaction verification with generic matchers'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) 1*studentDatabase.getStudentGrade('123') }
Un punto importante da notare qui è che puoi sempre mescolare e abbinare per quali argomenti sono noti e cosa non è noto. Ad esempio, nell'esempio seguente, stiamo convalidando l'interazione di un mock con gli argomenti effettivi e l'altro con gli abbinamenti sciolti.
# 3) Infine, vediamo uno scenario in cui possiamo accertare l'ordine di invocazione fittizia, ovvero quale ordine sono stati chiamati i mock quando viene eseguito il test.
A volte è essenziale convalidare il flusso di eventi quando ci sono più collaboratori / mock coinvolti nell'applicazione sotto test ed è utile per capire e convalidare che i metodi sono stati chiamati in una sequenza predeterminata.
def 'illustrate mocks for validating order'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.getStudentGrade('123') then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) }
Ciò può essere ottenuto semplicemente utilizzando più blocchi 'then:' nell'ordine delle aspettative di sequenza Mock. Se la sequenza menzionata non ha soddisfatto l'ordine effettivo di invocazione, viene generato un errore che specifica 'Ordine di invocazione errato'.
Ad esempio, se cambio l'ordine di quanto sopra poi istruzioni, l'esecuzione del test genererà un errore come mostrato di seguito.
Wrong invocation order for: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) (1 invocation) Last invocation: studentDatabase.updateStudentGrade('123', 'C')
Stubbing a Spock
Video tutorial
Abbiamo esplorato tutto sul Mocking, ora vediamo come definire gli stub sugli oggetti derisi. Lo stubbing non è altro che impostare risposte predefinite o predefinite alle invocazioni Mock per testare i diversi flussi / scenari dell'applicazione sotto test.
Pensalo come programmare un mock per restituire un valore predefinito quando è stato chiamato. Continueremo con la stessa app StudentGradeCalculator e bloccheremo le chiamate dell'interfaccia del database per testare diversi scenari.
Uno Stub è come un Mock che in un certo senso emula il comportamento dell'oggetto reale. Puoi semplicemente chiamarlo Mock programmato.
Stubbing Syntax
La sintassi per lo stubbing è di 2 operatori di spostamento a destra, ovvero ' >> '
Per impostare uno stub su qualsiasi chiamata, è possibile definirlo come segue:
StubbedObject.StubbedMethod(//argumentList) >> “Stubbed Response”
Vediamo ora i diversi scenari di stubbing con gli esempi.
# 1) Stubbing con parametri effettivi: Se gli argomenti sono noti in anticipo o se si desidera impostare lo stub solo quando l'invocazione è con argomenti specificati, è possibile utilizzare questo modo di specificare gli stub.
def 'illustrate stubs with exact matchers'() { given: studentDatabase.getStudentScores('123') >> (20F, 30F, 50F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' }
Qui puoi vedere che lo stub è stato impostato con un argomento esatto, ad esempio StudentId in questo caso come '123' (per qualsiasi altro valore lo stub non verrà richiamato e verrà restituita una risposta predefinita).
# 2) Stubbing con fiammiferi indulgenti: Se gli argomenti non sono noti (o non sono importanti), possiamo menzionarli liberamente come abbiamo fatto per i mock e la sintassi rimane la stessa, ovvero il trattino basso '_'.
def 'illustrate stubs with loose matchers'() { given: studentDatabase.getStudentScores(_ as String) >> (20F, 30F, 10F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' }
# 3) Vediamo un altro rapido esempio in cui abbiamo impostato lo stub per generare un'eccezione.
Questi scenari sono molto utili per convalidare la logica di gestione degli errori di un'applicazione sotto test (come nel mondo reale, generare tutte le eccezioni in realtà non è possibile ma potrebbe essere impostato un semplice stub per restituire qualunque eccezione vogliamo e poi affermarla in il blocco poi).
metodo java che accetta un array
def 'illustrate stubs with exceptions thrown'() { given: studentDatabase.getStudentScores(_ as String) >> {throw new RuntimeException()} when: studentReportGenerator.calculateStudentGrade('123') then: thrown(RuntimeException.class) }
Spiare a Spock
Le spie si basano su oggetti reali cioè hanno bisogno dell'implementazione dell'interfaccia e non dell'interfaccia astratta stessa. Le spie sono potenti e possono consentire di ottenere metodi reali chiamati per l'applicazione sotto test e verificare quali argomenti sono stati chiamati i metodi.
Le spie consentono anche di definire derisioni parziali sulle istanze degli oggetti spiati. cioè supponi di voler definire il comportamento di alcuni metodi sull'oggetto, quindi puoi e permetti che il resto venga chiamato come chiamate di metodi reali.
Questi sono in genere utili in una situazione in cui potrebbero esserci alcuni metodi di interfaccia che non sono implementati e ce ne sono pochi altri che sono completamente funzionanti. Quindi, tu come sviluppatore puoi scegliere di bloccare quelli non implementati e chiamare le implementazioni reali dei metodi funzionali.
Va notato che, per gli oggetti spiati, a meno che non siano definiti stub, il comportamento predefinito sarà chiamare la reale implementazione. Detto questo, le spie non dovrebbero essere chiamate frequentemente e tutta la copertura dello scenario può essere ottenuta utilizzando mock e stub e una combinazione di essi.
Vediamo alcuni esempi che utilizzano Spies nel framework Spock utilizzando lo stesso esempio di StudentGradeCalculator (Abbiamo creato una reale implementazione del StudentDatabase che è un'implementazione in memoria che utilizza HashMap per illustrare la chiamata a metodi reali e la restituzione di dati. Il codice sarà disponibile nell'ultima sezione del tutorial):
# 1) Spiare usando una combinazione di chiamate stub e metodi reali
def 'illustrate spies'() { given: StudentDatabase spiedStudentDatabase = Spy(StudentDatabase.class) def studentReportGenerator = new StudentReportGenerator(spiedStudentDatabase) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' 1*spiedStudentDatabase.getStudentGrade(_ as String) >> 'A' }
L'esempio sopra mostra la sintassi per la creazione di Spy utilizzando il framework Spock. Lo stub viene definito al momento della dichiarazione stessa.
Inoltre, le chiamate spiate possono essere verificate come illustrato nel blocco then (con corrispondenze di argomenti sciolti che possono essere definiti per argomenti specifici).
# 2) Spiare usando tutte le chiamate ai metodi reali
def 'illustrate spies with real method call'() { given: StudentDatabase spiedStudentDatabase = Spy(StudentDatabase.class) def studentReportGenerator = new StudentReportGenerator(spiedStudentDatabase) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' 1*spiedStudentDatabase.getStudentGrade('123') }
Nell'esempio sopra, poiché non abbiamo menzionato alcun comportamento stubbed, tutte le chiamate andranno all'implementazione reale.
Conclusione
In questo tutorial, abbiamo imparato tutto sulle tecniche integrate per Mock Stub e Spy utilizzando il framework Spock. Spock lo rende facile combinando queste caratteristiche come parte del framework stesso con una sintassi groovy più leggibile insieme al codice boilerplate minore.
I mock, gli stub e le spie sono ampiamente utilizzati nei test di unità per aumentare la copertura e il test o per convalidare la logica aziendale principale dell'applicazione sottoposta a test.
Codice sorgente per l'applicazione
StudentReportGenerator.java: questo è il metodo / applicazione in prova
package app.studentScores; import java.util.List; public class StudentReportGenerator { public IStudentDatabase studentDatabase; public StudentReportGenerator(IStudentDatabase studentDatabase) { this.studentDatabase = studentDatabase; } public String calculateStudentGrade(String studentId) { String grade; // check if grade is already there in database grade = studentDatabase.getStudentGrade(studentId); if(grade!=null && !grade.isEmpty()) { return grade; } List scoreList = studentDatabase.getStudentScores(studentId); Float totalScore = 0F; if(scoreList !=null) totalScore = scoreList.stream().reduce(0F,(a,b)->a+b); if(totalScore > 90) { grade = 'A'; } else if (totalScore > 80) { grade = 'B'; } else { grade = 'C'; } // update the calculated grade in database studentDatabase.updateStudentGrade(studentId, grade); return grade; } }
IStudentDatabase.java - Interfaccia database
package app.studentScores; import java.util.List; public interface IStudentDatabase { List getStudentScores(String studentId); void updateStudentGrade(String studentId, String grade); String getStudentGrade(String studentId); }
StudentDatabase.java - Implementazione InMemory dell'interfaccia IStudentDatabase.java
package app.studentScores; import java.util.*; public class StudentDatabase implements IStudentDatabase { private Map scoreMap; private Map gradeMap; public StudentDatabase() { this.scoreMap = new HashMap(); this.gradeMap = new HashMap(); scoreMap.put('123', Arrays.asList(40F, 30F, 30F)); scoreMap.put('456', Arrays.asList(10F, 10F, 30F)); gradeMap.put('123', 'C'); gradeMap.put('456', 'A'); } @Override public List getStudentScores(String studentId) { return scoreMap.get(studentId); } @Override public void updateStudentGrade(String studentId, String grade) { gradeMap.put(studentId,grade); } @Override public String getStudentGrade(String studentId) { return gradeMap.get(studentId); } }
Nel nostro prossimo tutorial, vedremo come integrare il framework Spock con altri framework e tecnologie di test.
Tutorial PREV | PROSSIMO Tutorial
Lettura consigliata
- Scrittura di unit test con Spock Framework
- Domande dell'intervista a Spock con risposte (le più popolari)
- Spock per integrazione e test funzionali con selenio
- Test basato sui dati o parametrizzato con Spock Framework
- Tutorial di Spock: test con Spock e Groovy
- Miglior serie di tutorial GRATUITI per C #: la guida definitiva a C # per principianti
- Test di carico con HP LoadRunner Tutorial
- Funzioni di data e ora in C ++ con esempi