spock mocking stubbing
Batjocorire, împiedicare și spionaj cu Spock:
Testare parametrizată în Spock Framework a fost explicată în detaliu în aceasta Seria de tutoriale de instruire pe Spock .
Batjocura și tăierea sunt unul dintre cele mai esențiale elemente de bază ale testelor extinse ale unității. Suportul pentru batjocură și înlocuire este ca vișinele de pe tort pentru un cadru.
Pentru cadrele existente, cum ar fi JUnit, JBehave, etc. suportul pentru simulări și butoane nu iese din cutie, prin urmare necesită ca un dezvoltator să utilizeze biblioteci terțe, precum Mockito, PowerMock, EasyMock etc., pentru a le utiliza în teste unitare.
Pentru a înțelege batjocurile și butoanele și cazurile lor de utilizare, puteți arunca o privire la seria noastră de Tutorial Mockito .
În acest tutorial, vom afla mai multe despre caracteristicile integrate de batjocură și stubbing integrate în biblioteca Spock, care la rândul lor ar permite utilizarea sintaxei Groovy mai ușoare și, prin urmare, reduce nevoia de a adăuga / include orice alte 3rdbiblioteci de petreceri.
Puteți include oricând alte cadre de batjocură în teste, întrucât toate codurile Java valide sunt și coduri Groovy valide.
Ce veți învăța:
- Cerere sub test
- Batjocoritor în Spock
- Stubbing în Spock
- Spionând în Spock
- Concluzie
- Cod sursă pentru aplicație
- Lectură recomandată
Cerere sub test
Să definim mai întâi un eșantion de aplicație Java, pe care îl vom testa folosind simulări și butoane în cadrul Spock.
Vom lucra la o aplicație StudentGradeCalculator care preia scorul total dintr-o bază de date abstractizată pentru un ID de student dat și are o logică simplă de atribuire a notelor în funcție de valoarea scorului total. Vom folosi o interfață de bază de date care are puține metode pentru a prelua și actualiza scorurile și notele elevilor.
aplicații Java în lumea reală
Codul aplicației va fi disponibil în ultima secțiune a acestui tutorial.
Batjocoritor în Spock
Tutorial video
În această secțiune, vom vedea cum să instanțiem și să inițializăm Mock-urile în cadrul Spock și cum să validăm interacțiunile pe mock, adică validarea apelurilor către mock-uri care au avut loc conform așteptărilor metodei testate.
Cu Mocks, nu trebuie să faceți o mulțime de configurări, dar puteți valida interacțiunile care s-au întâmplat cu obiectele simulate furnizate aplicației supuse testului.
Cu batjocurile, puteți face lucruri precum:
- Cu ce argumente au fost numiți batjocurile?
- Care a fost numărul total de invocații etc.?
- Constatând ordinea batjocurilor.
Să vedem un exemplu simplu de StudentGradeCalculator, în care furnizăm obiectul de implementare a bazei de date batjocorite și validăm interacțiunile cu Mock. Vom încerca să înțelegem caracteristicile de batjocură cu exemple simple.
Vă rugăm să rețineți că toate validările interacțiunii ar trebui să aibă loc în blocul „atunci” prin convenție.
Mai jos este codul pentru metoda testată (care va fi numit în „ când: ' bloc)
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) Validarea interacțiunilor cu argumente exacte: Să validăm mai întâi interacțiunile cu argumentele exact așteptate. Aici ne vom aștepta ca metodele batjocorite să fie apelate cu argumentele exacte (conform fluxului de execuție a metodei).
Aici ' baza de date student ”Este Mock-ul unei interfețe de baze de date pentru care validăm interacțiunile.
def 'illustrate mocks for interaction verification with arguments'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade('123','C') 1*studentDatabase.getStudentGrade('123') } După cum se arată mai sus, validăm cu argumentele exacte, astfel încât implementarea batjocorită trebuie să fi fost apelată cu. Orice modificare a acestor argumente va provoca eșecul testului și jurnalul de erori arată motivul adecvat.
Să încercăm să schimbăm nota în „ updateStudentGrade ”La„ A ”în loc de efectivul numit„ C ”și vedeți ce eroare primim atunci când testul este executat.
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') Va afișa o eroare precum „Prea puține invocații”, deoarece nu poate găsi invocarea falsă cu argumentele furnizate.
cum se deschide un fișier torentat pe Mac
#Două) Acum să vedem cum să validăm interacțiunile Mock fără a furniza valorile reale ale argumentelor, adică ceea ce ne interesează este doar să știm că simularea a fost invocată pe metodă, dar nu cu ce argumente.
Aceste tipuri de cerințe sunt cele mai frecvente în timpul scrierii testelor unitare pentru codul de producție real, deoarece nu este întotdeauna ușor să identificați argumentele reale care depind în esență de logica de bază a aplicației testate.
Sintaxa este simplă, trebuie doar să utilizați un subliniere „_” pentru un argument în care valoarea reală nu este cunoscută.
De exemplu, pentru a verifica orice valoare String, puteți doar să menționați „_ Ca Șir ”În locul unui argument din test și ar trebui să treacă pentru orice valoare a șirului (în mod similar pentru alte tipuri de date primitive și personalizate).
Să înțelegem acest lucru cu un exemplu
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 punct important de remarcat aici este că puteți oricând să amestecați și să potriviți pentru ce argumente sunt cunoscute și ceea ce nu este cunoscut. De exemplu, în exemplul de mai jos, validăm interacțiunea dintre o batjocură cu argumentele reale și cealaltă cu meciurile libere.
# 3) În cele din urmă, să vedem un scenariu în care putem stabili ordinea invocării simulate, adică la ce ordine au fost numite simulatorii când testul este executat.
Uneori este esențial să validați fluxul de evenimente atunci când există mai mulți colaboratori / batjocuri implicați în aplicația testată și este util să înțelegeți și să validați că metodele au fost apelate într-o secvență predeterminată.
def 'illustrate mocks for validating order'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.getStudentGrade('123') then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) } Acest lucru poate fi realizat prin simpla utilizare a mai multor blocuri „apoi:” în ordinea așteptărilor secvenței Mock. Dacă secvența menționată nu a îndeplinit ordinea efectivă de invocare, atunci se aruncă o eroare care detaliază „Ordine invocare greșită”.
De exemplu, dacă schimb ordinea celor de mai sus atunci , executarea testului va genera o eroare așa cum se arată mai jos.
Wrong invocation order for: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) (1 invocation) Last invocation: studentDatabase.updateStudentGrade('123', 'C') Stubbing în Spock
Tutorial video
Am explorat totul despre Mocking, acum să vedem cum să definim Stubs pe obiectele batjocorite. Stubbing nu este altceva decât setarea răspunsurilor predefinite sau predefinite pe invocațiile Mock pentru a testa diferitele fluxuri / scenarii ale aplicației supuse testului.
Gândiți-vă la programarea unei simulări pentru a returna o valoare predefinită atunci când a fost apelată. Vom continua cu aceeași aplicație StudentGradeCalculator și vom șterge apelurile interfeței bazei de date pentru a testa diferite scenarii.
Un butuc este ca o batjocură care emulează într-un fel comportamentul obiectului real. Puteți pur și simplu să-l apelați ca un Mock programat.
Sintaxa stubbing
Sintaxa pentru stubbing este de 2 operatori de schimbare dreapta - adică „ >> '
Pentru a seta un buton pentru orice apel, îl puteți defini după cum urmează:
StubbedObject.StubbedMethod(//argumentList) >> “Stubbed Response”Să înțelegem acum diferitele scenarii de stubing cu exemple.
# 1) Stubing cu parametrii reali: Dacă argumentele sunt cunoscute în prealabil sau dacă doriți să setați stub numai atunci când invocarea este cu argumente specificate, poate fi utilizat acest mod de a specifica stub-uri.
def 'illustrate stubs with exact matchers'() { given: studentDatabase.getStudentScores('123') >> (20F, 30F, 50F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' } Aici puteți vedea că stub-ul a fost setat cu un argument exact, adică StudentId în acest caz ca „123” (pentru orice altă valoare stub-ul nu va fi invocat și va exista un răspuns implicit returnat).
# 2) Îmbinarea cu chibrituri îngăduitoare: Dacă argumentele nu sunt cunoscute (sau nu sunt importante), atunci le putem menționa în mod vag, așa cum am făcut pentru jocuri de râs, iar sintaxa rămâne aceeași, adică subliniul „_”.
def 'illustrate stubs with loose matchers'() { given: studentDatabase.getStudentScores(_ as String) >> (20F, 30F, 10F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' } # 3) Să vedem un alt exemplu rapid în care configurăm un butuc pentru a face o excepție.
Aceste scenarii sunt foarte utile pentru validarea logicii de gestionare a erorilor unei aplicații supuse testului (ca în lumea reală, generarea tuturor excepțiilor nu este posibilă, dar ar putea fi configurat un simplu buton pentru a returna orice excepție dorim și apoi să o afirmăm în blocul de atunci).
cum se scrie un raport bun de eroare
def 'illustrate stubs with exceptions thrown'() { given: studentDatabase.getStudentScores(_ as String) >> {throw new RuntimeException()} when: studentReportGenerator.calculateStudentGrade('123') then: thrown(RuntimeException.class) } Spionând în Spock
Spionii se bazează pe obiecte reale adică au nevoie de implementarea interfeței și nu de interfața abstractă în sine. Spionii sunt puternici și vă pot permite să obțineți metode reale solicitate pentru aplicația testată și să verificați pentru ce argumente au fost solicitate metodele.
Spionii permit, de asemenea, definirea batjocurilor parțiale asupra instanțelor de obiect spionat. de exemplu, să presupunem că doriți să definiți comportamentul unor metode asupra obiectului, atunci puteți și permite ca restul să fie apelate ca apeluri de metode reale.
Acestea sunt de obicei utile într-o situație în care ar putea exista unele metode de interfață care nu sunt implementate și există puține altele care sunt pe deplin funcționale. Prin urmare, în calitate de dezvoltator, puteți alege să le eliminați pe cele neimplementate și să apelați implementările reale ale metodelor funcționale.
Trebuie remarcat faptul că, pentru obiectele Spied, cu excepția cazului în care sunt definite stub-uri, comportamentul implicit va fi apelarea implementării reale. Acestea fiind spuse, spionii nu ar trebui să fie chemați frecvent și toată acoperirea scenariului poate fi realizată folosind batjocuri și butucuri și o combinație a acestora.
Să vedem câteva exemple folosind Spies în cadrul Spock folosind același exemplu de StudentGradeCalculator (Am creat o implementare reală a StudentDatabase care este o implementare în memorie folosind HashMap pentru a ilustra apelarea metodelor reale și returnarea datelor. Codul va fi disponibil în ultima secțiune a tutorialului):
# 1) Spionaj folosind o combinație de apeluri cu stub și metode reale
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' } Exemplul de mai sus ilustrează sintaxa pentru crearea Spy folosind cadrul Spock. Stubul este definit la momentul declarației în sine.
De asemenea, apelurile spionate pot fi verificate așa cum este ilustrat în blocul de atunci (cu potriviri de argumente libere care pot fi definite pentru orice argumente specifice).
# 2) Spionaj folosind toate apelurile cu metode reale
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') } În exemplul de mai sus, deoarece nu am menționat niciun comportament obstacol, toate apelurile vor merge la implementarea reală.
Concluzie
În acest tutorial, am aflat totul despre tehnicile încorporate pentru a Mock Stub și Spy folosind cadrul Spock. Spock ușurează combinarea acestor caracteristici ca parte a cadrului în sine cu o sintaxă groovy mai lizibilă împreună cu codul mai mic al cazanului.
Mock-urile, butoanele și spionii sunt utilizate pe scară largă în testarea unitară pentru creșterea acoperirii și testarea sau validarea logicii de bază a aplicației supuse testului.
Cod sursă pentru aplicație
StudentReportGenerator.java - aceasta este metoda / aplicația testată
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 - Interfața bazei de date
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 - Implementarea InMemory a interfeței 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); } } În viitorul nostru tutorial, vom vedea cum să integrăm cadrul Spock cu alte cadre și tehnologii de testare.
Lectură recomandată
- Testarea unităților de scriere cu Spock Framework
- Întrebări de interviu cu răspunsuri Spock (Cele mai populare)
- Spock pentru integrare și testare funcțională cu seleniu
- Testare bazată pe date sau parametrizată cu Spock Framework
- Tutorial Spock: Testare cu Spock și Groovy
- Cea mai bună serie de tutoriale GRATUITE C #: Ghidul final C # pentru începători
- Testarea încărcării cu tutoriale HP LoadRunner
- Funcții de dată și oră în C ++ cu exemple