En guide til JMockit forventninger

1. Introduktion

Denne artikel er den anden rate i JMockit-serien. Det kan være en god idé at læse den første artikel, da vi antager, at du allerede er bekendt med JMockits grundlæggende.

I dag går vi dybere og fokuserer på forventningerne. Vi viser, hvordan man definerer mere specifik eller generisk argumentmatching og mere avancerede måder at definere værdier på.

2. Matching af argumentværdier

Følgende tilgange gælder begge for Forventninger såvel som Verifikationer.

2.1. "Eventuelle" felter

JMockit tilbyder et sæt hjælpefelter til at gøre argumenttilpasning mere generisk. Et af disse værktøjer er anyX felter.

Disse vil kontrollere, at enhver værdi blev videregivet, og at der er en for hver primitive type (og den tilsvarende indpakningsklasse), en for strenge og en "universal" af typen Objekt.

Lad os se et eksempel:

offentlig grænseflade ExpectationsCollaborator {String methodForAny1 (String s, int i, Boolean b); ugyldig metodeForAny2 (Long l, List lst); } @Test offentlig ugyldig test (@Mocked ExpectationsCollaborator mock) kaster undtagelse {new Expectations () {{mock.methodForAny1 (anyString, anyInt, anyBoolean); resultat = "enhver"; }}; Assert.assertEquals ("enhver", mock.methodForAny1 ("barfooxyz", 0, Boolean.FALSE)); mock.methodForAny2 (2L, ny ArrayList ()); nye FullVerifications () {{mock.methodForAny2 (anyLong, (List) any); }}; }

Du skal tage højde for, at når du bruger nogen felt, skal du kaste det til den forventede type. Den komplette liste over felter findes i dokumentationen.

2.2. "Med" -metoder

JMockit giver også flere metoder til at hjælpe med generisk argument matching. Det er de medX metoder.

Disse giver mulighed for en lidt mere avanceret matching end anyX felter. Vi kan se et eksempel her, hvor vi definerer en forventning til en metode, der udløses med en streng, der indeholder foo, et heltal, der ikke er lig med 1, et ikke-nul Boolsk og enhver forekomst af Liste klasse:

offentlig grænseflade ExpectationsCollaborator {String methodForWith1 (String s, int i); ugyldig metodeForWith2 (boolsk b, liste l); } @Test offentlig ugyldig testForWith (@Mocked ExpectationsCollaborator mock) kaster Undtagelse {new Expectations () {{mock.methodForWith1 (withSubstring ("foo"), withNotEqual (1)); resultat = "med"; }}; assertEquals ("med", mock.methodForWith1 ("barfooxyz", 2)); mock.methodForWith2 (Boolean.TRUE, ny ArrayList ()); nye verifikationer () {{mock.methodForWith2 (withNotNull (), withInstanceOf (List.class)); }}; }

Du kan se den komplette liste over medX metoder på JMockits dokumentation.

Tag højde for, at den særlige med (delegeret) og withArgThat (Matcher) vil blive dækket af deres eget underafsnit.

2.3. Null er ikke Null

Noget der er godt at forstå hurtigere end senere er det nul bruges ikke til at definere et argument for hvilket nul er overført til en hån.

Rent faktisk, nul bruges som syntaktisk sukker for at definere, at ethvert objekt vil blive sendt (så det kun kan bruges til parametre af referencetype). For specifikt at kontrollere, at en given parameter modtager nul reference, den withNull () matcher kan bruges.

I det næste eksempel definerer vi adfærd for en mock, der skal udløses, når de argumenter, der sendes, er: en hvilken som helst streng, en hvilken som helst liste og en nul reference:

offentlig grænseflade ExpectationsCollaborator {String methodForNulls1 (String s, List l); ugyldig metodeForNulls2 (streng s, liste l); } @Test offentlig ugyldig testWithNulls (@Mocked ExpectationsCollaborator mock) {new Expectations () {{mock.methodForNulls1 (anyString, null); resultat = "null"; }}; assertEquals ("null", mock.methodForNulls1 ("blablabla", ny ArrayList ())); mock.methodForNulls2 ("blablabla", null); nye verifikationer () {{mock.methodForNulls2 (anyString, (List) withNull ()); }}; }

Bemærk forskellen: nul betyder enhver liste og withNull () betyder a nul henvisning til en liste. Dette undgår især behovet for at kaste værdien til den deklarerede parametertype (se at det tredje argument skulle kastes, men ikke det andet).

Den eneste betingelse for at kunne bruge dette er, at mindst en eksplicit argumentmatcher var blevet brugt til forventningen (enten a med metode eller en nogen Mark).

2.4. "Times" felt

Nogle gange vil vi begrænseantallet af påkaldelser forventes for en hånet metode. Til dette har JMockit de reserverede ord gange, minTimes og maxTimes (alle tre tillader kun ikke-negative heltal).

offentlig grænseflade ExpectationsCollaborator {void methodForTimes1 (); ugyldig metodeForTimes2 (); ugyldig metodeForTimes3 (); } @Test offentlig ugyldig testWithTimes (@Mocked ExpectationsCollaborator mock) {new Expectations () {{mock.methodForTimes1 (); gange = 2; mock.methodForTimes2 (); }}; mock.methodForTimes1 (); mock.methodForTimes1 (); mock.methodForTimes2 (); mock.methodForTimes3 (); mock.methodForTimes3 (); mock.methodForTimes3 (); nye verifikationer () {{mock.methodForTimes3 (); minTimes = 1; maxTimes = 3; }}; }

I dette eksempel har vi defineret nøjagtigt to påkaldelser (ikke en, ikke tre, nøjagtigt to) af methodForTimes1 () skal udføres ved hjælp af linjen gange = 2;.

Derefter brugte vi standardadfærden (hvis der ikke gives nogen gentagelsesbegrænsning minTimes = 1; bruges) til at definere, at mindst en påkaldelse skal gøres for methodForTimes2 ().

Endelig ved hjælp af minTimes = 1; efterfulgt af maxTimes = 3; vi definerede, at mellem en og tre påkald ville forekomme methodForTimes3 ().

Tag højde for, at begge dele minTimes og maxTimes kan specificeres for den samme forventning, så længe minTimes tildeles først. På den anden side, gange kan kun bruges alene.

2.5. Tilpasning af tilpasset argument

Nogle gange er matchning af argumenter ikke så direkte som blot at angive en værdi eller bruge nogle af de foruddefinerede hjælpeprogrammer (anyX eller medX).

I de sager er JMockit afhængig af Hamcrest Matcher interface. Du skal bare definere en matcher til det specifikke testscenarie og bruge den matcher med en medArgThat () opkald.

Lad os se et eksempel til at matche en bestemt klasse med et bestået objekt:

offentlig grænseflade ExpectationsCollaborator {void methodForArgThat (Object o); } public class Model {public String getInfo () {return "info"; }} @Test offentlig ugyldig testCustomArgumentMatching (@Mocked ExpectationsCollaborator mock) {new Expectations () {{mock.methodForArgThat (withArgThat (new BaseMatcher () {@Override public boolean matches (Object item) {return item instanceof Model && "info"). er lig med (((Model) element) .getInfo ());} @ Overstyr offentlig tomrum beskriverTil (Beskrivelse af beskrivelse) {}})); }}; mock.methodForArgThat (ny model ()); }

3. Returnerende værdier

Lad os nu se på returværdierne; husk at følgende fremgangsmåder kun gælder for Forventninger da der ikke kan defineres nogen returværdier for Verifikationer.

3.1. Resultat og retur (...)

Når du bruger JMockit, har du tre forskellige måder at definere det forventede resultat af påkaldelsen af ​​en hånet metode. Af alle tre taler vi nu om de to første (de enkleste), der helt sikkert vil dække 90% af hverdagsbrugssagerne.

Disse to er de resultat felt og returnerer (Objekt ...) metode:

  • Med resultat felt, kan du definere en returværdi for enhver ikke-ugyldig returneret spottet metode. Denne returværdi kan også være en undtagelse, der skal kastes (denne gang fungerer det for både ikke-ugyldige og ugyldige returmetoder).
    • Flere resultat feltopgaver kan udføres for at vende tilbage mere end en værdi til mere end en metodeopkald (du kan blande både returværdier og fejl, der skal kastes).
    • Den samme adfærd opnås ved tildeling til resultat en liste eller en række værdier (af samme type end returtypen for den spottede metode, INGEN undtagelser her).
  • Det returnerer (Objekt ...) metode er syntaktisk sukker for at returnere flere værdier på samme tid.

Dette vises lettere med et kodestykke:

offentlig grænseflade ExpectationsCollaborator {String methodReturnsString (); int-metodeReturnsInt (); } @Test offentlig ugyldig testResultAndReturns (@Mocked ExpectationsCollaborator mock) {new Expectations () {{mock.methodReturnsString (); resultat = "foo"; resultat = ny undtagelse (); resultat = "bar"; returnerer ("foo", "bar"); mock.methodReturnsInt (); resultat = ny int [] {1, 2, 3}; resultat = 1; }}; assertEquals ("Skal returnere foo", "foo", mock.methodReturnsString ()); prøv {mock.methodReturnsString (); fail ("Bør ikke nå her"); } catch (Undtagelse e) {// NOOP} assertEquals ("Skal returnere bar", "bar", mock.methodReturnsString ()); assertEquals ("Skal returnere 1", 1, mock.methodReturnsInt ()); assertEquals ("Skal returnere 2", 2, mock.methodReturnsInt ()); assertEquals ("Skal returnere 3", 3, mock.methodReturnsInt ()); assertEquals ("Skal returnere foo", "foo", mock.methodReturnsString ()); assertEquals ("Skal returnere bar", "bar", mock.methodReturnsString ()); assertEquals ("Skal returnere 1", 1, mock.methodReturnsInt ()); }

I dette eksempel har vi defineret det for de første tre opkald til methodReturnsString () de forventede afkast er (i rækkefølge) “Foo”, en undtagelse og "bar". Vi opnåede dette ved hjælp af tre forskellige opgaver til resultat Mark.

Så videre linje 14, definerede vi det for det fjerde og femte opkald, “Foo” og "bar" skal returneres ved hjælp af returnerer (Objekt ...) metode.

Til methodReturnsInt () definerede vi videre linje 13 for at returnere 1, 2 og til sidst 3 ved at tildele en matrix med de forskellige resultater til resultat felt og videre linje 15 vi definerede at returnere 1 ved en simpel tildeling til resultat Mark.

Som du kan se, er der flere måder at definere returværdier for mocked metoder på.

3.2. Delegatorer

For at afslutte artiklen skal vi dække den tredje måde at definere returværdien på: Delegeret interface. Denne grænseflade bruges til at definere mere komplekse returværdier, når man definerer mocked metoder.

Vi vil se et eksempel på blot forklaringen:

offentlig grænseflade ExpectationsCollaborator {int methodForDelegate (int i); } @Test offentlig ugyldighed testDelegate (@Mocked ExpectationsCollaborator mock) {new Expectations () {{mock.methodForDelegate (anyInt); resultat = ny delegat () {int delegeret (int i) kaster undtagelse {hvis (i <3) {returnerer 5; } ellers {kast ny undtagelse (); }}} }}; assertEquals ("Skal returnere 5", 5, mock.methodForDelegate (1)); prøv {mock.methodForDelegate (3); fail ("Bør ikke nå her"); } fangst (undtagelse e) {}} 

Måden at bruge en delegator er at oprette en ny instans for den og tildele den til en vender tilbage Mark. I denne nye forekomst skal du oprette en ny metode med de samme parametre og returtype end den spottede metode (du kan bruge ethvert navn til den). Inde i denne nye metode skal du bruge den implementering, du ønsker, for at returnere den ønskede værdi.

I eksemplet lavede vi en implementering, hvor 5 skal returneres, når værdien, der sendes til den spottede metode, er mindre end 3 og en undtagelse kastes ellers (bemærk, at vi var nødt til at bruge gange = 2; således at den anden påkaldelse forventes, da vi mistede standardadfærden ved at definere en returværdi).

Det kan virke som en hel del kode, men i nogle tilfælde er det den eneste måde at opnå det resultat, vi ønsker.

4. Konklusion

Med dette viste vi praktisk talt alt, hvad vi har brug for for at skabe forventninger og verifikationer til vores daglige tests.

Vi offentliggør selvfølgelig flere artikler om JMockit, så hold dig opdateret for at lære endnu mere.

Og som altid kan den fulde implementering af denne vejledning findes på GitHub-projektet.

4.1. Artikler i serien

Alle artikler i serien:

  • JMockit 101
  • En guide til JMockit forventninger
  • JMockit avanceret brug