spock mocking stubbing
Spott, stubbing og spionering med Spock:
Parameterisert testing i Spock Framework ble forklart i detalj i dette Series of Training Tutorials on Spock .
Spott og stubbing er en av de viktigste byggesteinene i omfattende enhetstester. Støtte for hån og subbing er som kirsebæret på kaken for et rammeverk.
For eksisterende rammer som JUnit, JBehave osv. Kommer ikke støtte for mocks og stubs ut av esken, og det krever derfor at en utvikler bruker tredjepartsbiblioteker som Mockito, PowerMock, EasyMock, etc. for å kunne bruke dem i enhetstester.
For å forstå spotter og stubber og brukssakene deres, kan du ta en titt på vår serie av Mockito tutorial .
I denne opplæringen vil vi lære mer om de innebygde Mocking og Stubbing-funksjonene integrert i selve Spock-biblioteket, som igjen vil gjøre det mulig å bruke den enklere Groovy-syntaksen og derved redusere behovet for å legge til / inkludere andre 3rdfestbibliotek.
Du kan alltid inkludere andre Mocking-rammer i testene dine, da all gyldig Java-kode også er gyldig Groovy-kode.
Hva du vil lære:
- Søknad under test
- Hånlig i Spock
- Stubbing i Spock
- Spionere i Spock
- Konklusjon
- Kildekode for applikasjonen
- Anbefalt lesing
Søknad under test
La oss først definere et eksempel på Java-applikasjon, som vi skal teste ved hjelp av mocks og stubs i Spock-rammeverket.
Vi vil jobbe med en StudentGradeCalculator-app som tar totalpoengsummen fra en abstrakt database for en gitt student-ID og har en enkel logikk for karakteroppgavene, avhengig av verdien av totalpoengsummen. Vi vil bruke et databasegrensesnitt som har få metoder for å hente og oppdatere studentens poeng og karakterer.
Koden for applikasjonen vil være tilgjengelig i den siste delen av denne veiledningen.
Hånlig i Spock
Videoopplæring
I denne delen vil vi se hvordan man kan starte og initialisere Mocks i Spock-rammeverket og hvordan man kan validere interaksjoner på mocken, dvs. validering av samtalene til mocks skjedde i henhold til forventningene til metoden som testes.
Med Mocks trenger du ikke å gjøre mange oppsett, men du kan validere interaksjonene som skjedde med de mock-objektene som ble levert til applikasjonen som ble testet.
Med mocks kan du gjøre ting som:
- Hvilke argumenter ble hånene kalt med?
- Hva var det totale antallet påkallelser osv.?
- Å fastslå rekkefølgen på spotter.
La oss se et enkelt eksempel på StudentGradeCalculator, hvor vi leverer det spottede databasens implementeringsobjekt og validerer interaksjonene med Mock. Vi vil prøve å forstå spottende funksjoner med enkle eksempler.
Vær oppmerksom på at alle samhandlingsvalideringer skal skje i 'da' -blokken etter konvensjon.
desktop support engineer intervju spørsmål og svar
Nedenfor er koden for metoden som testes (som vil bli kalt i “ når: ”Blokk)
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) Validering av interaksjonene med eksakte argumenter: La oss først validere interaksjonene med de nøyaktig forventede argumentene. Her vil vi forvente at de spottede metodene blir kalt med de nøyaktige argumentene (i henhold til metodeutførelsesflyten).
Her “ studentDatabase ”Er Mock of a database interface som vi validerer interaksjonene for.
def 'illustrate mocks for interaction verification with arguments'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade('123','C') 1*studentDatabase.getStudentGrade('123') }
Som vist ovenfor validerer vi med de eksakte argumentene, slik at den spottede implementeringen må ha blitt ringt med. Eventuelle endringer i disse argumentene vil føre til at testen mislykkes, og feilloggen viser riktig årsak.
La oss prøve å endre karakteren i ' updateStudentGrade ”Til” A ”i stedet for den som faktisk kalles“ C ”og se hvilken feil vi får når testen utføres.
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')
Det vil vise en feil som 'For få påkallinger', da den ikke finner Mock-påkallelsen med de medfølgende argumentene.
bugs livssyklus i programvaretesting
#to) La oss nå se hvordan vi validerer Mock-interaksjonene uten å levere de faktiske argumentverdiene, dvs. det vi er interessert i er bare å vite at mocken ble påkalt på metoden, men ikke med hvilke argumenter.
Denne typen krav er vanligst når man skriver enhetstester for den faktiske produksjonskoden, da det ikke alltid er lett å identifisere de faktiske argumentene som i det vesentlige avhenger av kjernevirksomhetslogikken til applikasjonen som testes.
Syntaksen er enkel, du trenger bare å bruke en understreking “_” for et argument der den faktiske verdien ikke er kjent.
For eksempel, for å sjekke om det er en strengverdi, kan du bare nevne “_ Som streng ”I stedet for et argument i testen, og den skal passere for en hvilken som helst strengverdi (på samme måte for andre primitive så vel som tilpassede datatyper).
La oss forstå dette med et eksempel
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') }
Et viktig poeng å merke seg her er at du alltid kan blande og matche for hvilke argumenter som er kjent og hva som ikke er kjent. For eksempel, i eksemplet nedenfor, validerer vi samspillet mellom en mock med de faktiske argumentene og den andre med de løse matcherne.
# 3) Til slutt, la oss se et scenario der vi kan fastslå rekkefølgen på mock-påkallelse, dvs. hvilken rekkefølge mockene ble kalt når testen ble utført.
Noen ganger er det viktig å validere strømmen av hendelser når det er flere samarbeidspartnere / mocks involvert i applikasjonen som testes, og det er nyttig å forstå og validere at metodene ble kalt i en forhåndsbestemt rekkefølge.
def 'illustrate mocks for validating order'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.getStudentGrade('123') then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) }
Dette kan oppnås ved å bruke flere “then:” blokker i rekkefølgen av Mock-sekvensforventningene. Hvis den nevnte sekvensen ikke oppfyller den faktiske anropsrekkefølgen, kastes en feil som beskriver 'Feil anropsrekkefølge'.
For eksempel hvis jeg endrer rekkefølgen på ovennevnte deretter uttalelser, vil testutførelsen kaste en feil som vist nedenfor.
Wrong invocation order for: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) (1 invocation) Last invocation: studentDatabase.updateStudentGrade('123', 'C')
Stubbing i Spock
Videoopplæring
Vi utforsket alt om Mocking, la oss nå se hvordan vi kan definere stubber på de hånede objektene. Stubbing er ikke annet enn å sette opp forhåndsdefinerte eller hermetiske svar på Mock-anropene for å teste de forskjellige flytene / scenariene til applikasjonen som testes.
Tenk på det som å programmere en mock for å returnere en forhåndsdefinert verdi når den ble kalt. Vi vil fortsette med den samme StudentGradeCalculator-appen og stoppe anropene til databasegrensesnittet for å teste forskjellige scenarier.
En stub er som en Mock som på en måte etterligner oppførselen til det virkelige objektet. Du kan ganske enkelt kalle det som en programmert Mock.
Stubbing Syntax
Syntaksen for stubbing er to høyre skiftoperatører - dvs. “ >> '
For å sette en stub på en samtale, kan du definere den som følger:
StubbedObject.StubbedMethod(//argumentList) >> “Stubbed Response”
La oss nå forstå de forskjellige stubbescenariene med eksempler.
#1) Stubbing med faktiske parametere: Hvis argumentene er kjent på forhånd, eller hvis du bare vil angi stubbe når påkallingen er med spesifiserte argumenter, kan denne måten å spesifisere stubber brukes på.
def 'illustrate stubs with exact matchers'() { given: studentDatabase.getStudentScores('123') >> (20F, 30F, 50F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' }
Her kan du se at stubben er angitt med et nøyaktig argument, dvs. StudentId i dette tilfellet som '123' (for en hvilken som helst annen verdi blir stubben ikke påkalt, og det vil bli returnert et standardsvar).
# 2) Stubbing med milde matchere: Hvis argumentene ikke er kjent (eller ikke er viktige), kan vi nevne dem løst som vi gjorde for mocks, og syntaksen forblir den samme, dvs. understrekningen “_”.
def 'illustrate stubs with loose matchers'() { given: studentDatabase.getStudentScores(_ as String) >> (20F, 30F, 10F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' }
# 3) La oss se et annet raskt eksempel der vi setter opp stub for å kaste et unntak.
Disse scenariene er veldig nyttige for å validere feilhåndteringslogikken til en applikasjon som testes (som i den virkelige verden, generering av alle unntakene er faktisk ikke mulig, men en enkel stub kan settes opp for å returnere det unntaket vi ønsker og deretter hevde det i den daværende blokken).
hvordan lage en kopi av en array-java
def 'illustrate stubs with exceptions thrown'() { given: studentDatabase.getStudentScores(_ as String) >> {throw new RuntimeException()} when: studentReportGenerator.calculateStudentGrade('123') then: thrown(RuntimeException.class) }
Spionere i Spock
Spioner er basert på virkelige gjenstander dvs. de trenger grensesnittimplementeringen og ikke selve det abstrakte grensesnittet. Spioner er kraftige, og de kan tillate deg å få reelle metoder som kalles for applikasjonen under test og verifisere hvilke argumenter metodene ble etterlyst.
Spioner tillater også å definere delvise mocks på forekomster av spionerte objekter. dvs. anta at du vil definere oppførselen til noen metoder på objektet, så kan du og la resten kalles som virkelige metodesamtaler.
Disse er vanligvis nyttige i en situasjon der det kan være noen metoder for grensesnitt som ikke er implementert, og det er få andre som er fullt funksjonelle. Derfor kan du som utvikler velge å stoppe de ikke-implementerte og kalle de virkelige implementeringene av de funksjonelle metodene.
Det skal bemerkes at for spionerte objekter, med mindre stubbe er definert, vil standard oppførsel være å kalle den virkelige implementeringen. Når det er sagt, bør ikke spioner ofte kalles, og all scenariodekning kan oppnås ved hjelp av mocks og stubs og en kombinasjon av dem.
La oss se noen eksempler på Spies i Spock-rammeverket ved hjelp av det samme eksemplet StudentGradeCalculator (Vi har laget en reell implementering av StudentDatabase som er en implementering i minnet som bruker HashMap for å illustrere kalle virkelige metoder og returnere data. Koden vil være tilgjengelig i den siste delen av opplæringen):
# 1) Spionering ved hjelp av en kombinasjon av stub og ekte metodesamtaler
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' }
Ovennevnte eksempel illustrerer syntaksen for å lage Spy ved hjelp av Spock-rammeverket. Stubben er definert på selve erklæringstiden.
De spionerte samtalene kan også bekreftes som illustrert i den da blokken (med løse argumentmatchere som kan defineres for spesifikke argumenter).
# 2) Spionering ved hjelp av alle virkelige metodesamtaler
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') }
I eksemplet ovenfor, da vi ikke har nevnt noe stubed atferd, vil alle samtalene gå til den virkelige implementeringen.
Konklusjon
I denne opplæringen lærte vi alt om de innebygde teknikkene til Mock Stub and Spy ved hjelp av Spock-rammeverket. Spock gjør det enkelt ved å kombinere disse funksjonene som en del av selve rammeverket med en mer lesbar groovy syntaks sammen med den mindre kokerplatekoden.
Mocks, Stubs og Spies brukes mye i enhetstesting for å øke dekning og testing eller validering av kjernevirksomhetslogikken til applikasjonen som testes.
Kildekode for applikasjonen
StudentReportGenerator.java - dette er metoden / applikasjonen som testes
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 - Database-grensesnitt
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 - InMemory-implementering av IStudentDatabase.java-grensesnittet
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); } }
I vår kommende opplæring vil vi se hvordan vi kan integrere Spock-rammeverket med andre testrammer og teknologier.
PREV Opplæring | NESTE veiledning
Anbefalt lesing
- Skriveenhetstester med Spock Framework
- Spock Intervjuespørsmål med svar (mest populære)
- Spock for integrering og funksjonstesting med selen
- Datadrevet eller parametrisert testing med Spock Framework
- Spock Tutorial: Testing With Spock And Groovy
- Beste GRATIS C # opplæringsserie: The Ultimate C # Guide For Beginners
- Lastetesting med HP LoadRunner-opplæringsprogrammer
- Funksjoner for dato og tid i C ++ med eksempler