JMockit avanceret brug

1. Introduktion

I denne artikel går vi ud over JMockit-grundlæggende, og vi begynder at se på nogle avancerede scenarier, såsom:

  • Falske (eller MockUp API)
  • Det Afkapsling utility klasse
  • Sådan mockes mere end en grænseflade ved kun at bruge en mock
  • Sådan genbruges forventninger og verifikationer

Hvis du vil opdage JMockits grundlæggende, skal du tjekke andre artikler fra denne serie. Du kan finde relevante links nederst på siden.

2. Maven-afhængighed

Først skal vi tilføje jmockit-afhængighed til vores projekt:

 org.jmockit jmockit 1.41 

Dernæst fortsætter vi med eksemplerne.

3. Hån af private metoder / indre klasser

Hån og test af private metoder eller indre klasser betragtes ofte ikke som god praksis.

Ræsonnementet bag det er, at hvis de er private, skal de ikke testes direkte, da de er klassens inderste tarm, men nogle gange skal det stadig gøres, især når det drejer sig om ældre kode.

Med JMockit har du to muligheder for at håndtere disse:

  • Det MockUp API til at ændre den reelle implementering (i det andet tilfælde)
  • Det Afkapsling utility class for at kalde en hvilken som helst metode direkte (i det første tilfælde)

Alle følgende eksempler udføres for den følgende klasse, og vi antager, at de køres på en testklasse med samme konfiguration som den første (for at undgå at gentage kode):

offentlig klasse AdvancedCollaborator {int i; private int privateField = 5; // standardkonstruktør udeladt offentlig AdvancedCollaborator (strengstreng) kaster undtagelse {i = streng.længde (); } public String methodThatCallsPrivateMethod (int i) {return privateMethod () + i; } public int methodThatReturnsThePrivateField () {return privateField; } privat streng privateMethod () {returner "standard:"; } klasse InnerAdvancedCollaborator {...}}

3.1. Faking With MockUp

JMockits Mockup API yder support til oprettelse af falske implementeringer eller mock-ups. Typisk er en mock-up målretter mod et par metoder og / eller konstruktører i klassen, der skal falses, mens de fleste andre metoder og konstruktører ikke ændres. Dette giver mulighed for en komplet omskrivning af en klasse, så enhver metode eller konstruktør (med enhver adgangsmodifikator) kan målrettes.

Lad os se, hvordan vi kan omdefinere privateMethod () ved hjælp af Mockups API:

@RunWith (JMockit.class) offentlig klasse AdvancedCollaboratorTest {@Tested privat AdvancedCollaborator mock; @Test offentlig ugyldig testToMockUpPrivateMethod () {new MockUp () {@Mock private String privateMethod () {return "mocked:"; }}; String res = mock.methodThatCallsPrivateMethod (1); assertEquals ("mocked: 1", res); }}

I dette eksempel definerer vi et nyt MockUp til AdvancedCollaborator klasse ved hjælp af @Mock kommentar på en metode med matchende signatur. Efter dette vil opkald til denne metode blive delegeret til vores hånede.

Vi kan også bruge dette til mock-up konstruktøren af ​​en klasse, der har brug for specifikke argumenter eller konfiguration for at forenkle testene:

@Test offentlig ugyldighed testToMockUpDifficultConstructor () kaster undtagelse {new MockUp () {@Mock public void $ init (Invocation invocation, String string) {((AdvancedCollaborator) invocation.getInvokedInstance ()). I = 1; }}; AdvancedCollaborator coll = ny AdvancedCollaborator (null); assertEquals (1, coll.i); }

I dette eksempel kan vi se, at for konstruktionsmocking skal du spotte $ init metode. Du kan videregive et ekstra argument af typen Påkaldelse, med hvilken du kan få adgang til oplysninger om påkaldelse af den spottede metode, herunder den forekomst, som påkaldelsen udføres til.

3.2. Bruger Afkapsling Klasse

JMockit inkluderer en testværktøjsklasse: Afkapsling. Som navnet antyder, bruges det til at afkapsle en tilstand af et objekt, og ved hjælp af det kan du forenkle testningen ved at få adgang til felter og metoder, som ellers ikke kunne fås.

Du kan påberåbe en metode:

@Test offentlig ugyldighed testToCallPrivateMethodsDirectly () {Objektværdi = Deencapsulation.invoke (mock, "privateMethod"); assertEquals ("standard:", værdi); }

Du kan også indstille felter:

@Test offentlig ugyldig testToSetPrivateFieldDirectly () {Deencapsulation.setField (mock, "privateField", 10); assertEquals (10, mock.methodThatReturnsThePrivateField ()); }

Og få felter:

@Test offentlig ugyldighed testToGetPrivateFieldDirectly () {int værdi = Deencapsulation.getField (mock, "privateField"); assertEquals (5, værdi); }

Og opret nye forekomster af klasser:

@Test offentlig ugyldig testToCreateNewInstanceDirectly () {AdvancedCollaborator coll = Deencapsulation .newInstance (AdvancedCollaborator.class, "foo"); assertEquals (3, coll.i); }

Selv nye forekomster af indre klasser:

@Test offentlig ugyldig testToCreateNewInnerClassInstanceDirectly () {InnerCollaborator inner = Deencapsulation .newInnerInstance (InnerCollaborator.class, mock); assertNotNull (indre); }

Som du kan se, er Afkapsling klasse er yderst nyttig, når man tester lufttætte klasser. Et eksempel kan være at indstille afhængigheder for en klasse, der bruger @Autowired annoteringer på private felter og har ingen settere til dem, eller at enhedsteste indre klasser uden at skulle afhænge af den offentlige grænseflade i containerklassen.

4. Spotte flere grænseflader i samme mock

Lad os antage, at du vil teste en klasse - endnu ikke implementeret - men du ved med sikkerhed, at den vil implementere flere grænseflader.

Normalt ville du ikke være i stand til at teste den nævnte klasse, før du implementerer den, men med JMockit har du evnen til at forberede tests på forhånd ved at håne mere end en grænseflade ved hjælp af et mock-objekt.

Dette kan opnås ved at bruge generiske stoffer og definere en type, der udvider flere grænseflader. Denne generiske type kan enten defineres for en hel testklasse eller kun for en testmetode.

For eksempel skal vi oprette en mock til grænseflader Liste og Sammenlignelig to måder:

@RunWith (JMockit.class) offentlig klasse AdvancedCollaboratorTest> {@Mocked privat MultiMock multiMock; @Test offentlig ugyldighed testOnClass () {nye forventninger () {{multiMock.get (5); resultat = "foo"; multiMock.compareTo ((liste) enhver); resultat = 0; }}; assertEquals ("foo", multiMock.get (5)); assertEquals (0, multiMock.compareTo (ny ArrayList ())); } @Test offentligt > ugyldig testOnMethod (@Mocked M mock) {nye forventninger () {{mock.get (5); resultat = "foo"; mock.compareTo ((List) any); resultat = 0; }}; assertEquals ("foo", mock.get (5)); assertEquals (0, mock.compareTo (ny ArrayList ())); }}

Som du kan se i linje 2, kan vi definere en ny testtype for hele testen ved hjælp af generiske på klassens navn. Den vej, MultiMock vil være tilgængelig som en type, og du vil være i stand til at oprette mocks til det ved hjælp af en af ​​JMockits annoteringer.

I linier fra 7 til 18 kan vi se et eksempel ved hjælp af en mock af en multi-klasse defineret for hele testklassen.

Hvis du har brug for multi-interface mock til kun en test, kan du opnå dette ved at definere den generiske type på metodesignaturen og videresende en ny mock af den nye generiske som testmetode argumentet. I linie 20 til 32 kan vi se et eksempel på at gøre det for den samme testede adfærd som i den foregående test.

5. Genbrug af forventninger og verifikationer

I sidste ende, når du tester klasser, kan du støde på tilfælde, hvor du gentager det samme Forventninger og / eller Verifikationer igen og igen. For at lette det kan du let genbruge begge dele.

Vi forklarer det ved et eksempel (vi bruger klasser Model, samarbejdspartnerog Kunstner fra vores JMockit 101-artikel):

@RunWith (JMockit.class) offentlig klasse ReusingTest {@Injectable private Collaborator collaborator; @Mocked privat model model; @Tested privat performer performer; @Før offentlig tomrumsopsætning () {nye forventninger () {{model.getInfo (); resultat = "foo"; minTimes = 0; collaborator.collaborate ("foo"); resultat = sandt; minTider = 0; }}; } @ Test offentlig ugyldig testWithSetup () {performer.perform (model); verificereTrueCalls (1); } beskyttet ugyldig verificereTrueCalls (int-opkald) {new Verifications () {{collaborator.receive (true); gange = opkald; }}; } sidste klasse TrueCallsVerification udvider verifikationer {public TrueCallsVerification (int calls) {collaborator.receive (true); gange = opkald; }} @ Test offentlig ugyldig testWithFinalClass () {performer.perform (model); nye TrueCallsVerification (1); }}

I dette eksempel kan du se i linjer fra 15 til 18, at vi forbereder en forventning til hver test, så det model.getInfo () vender altid tilbage “Foo” og for samarbejdspartner. samarbejde() at altid forvente “Foo” som argumentet og vender tilbage rigtigt. Vi sætter minTimes = 0 erklæring, så ingen fejl vises, når de ikke faktisk bruges i test.

Vi har også oprettet metode verificereTrueCalls (int) for at forenkle verifikationer til samarbejdspartner. modtager (boolsk) metode, når det godkendte argument er rigtigt.

Endelig kan du også oprette nye typer specifikke forventninger og verifikationer, der blot udvider nogen af Forventninger eller Verifikationer klasser. Derefter definerer du en konstruktør, hvis du har brug for at konfigurere adfærden og oprette en ny forekomst af den nævnte type i en test, som vi gør i linjer fra 33 til 43.

6. Konklusion

Med denne del af JMockit-serien har vi berørt adskillige avancerede emner, der helt sikkert vil hjælpe dig med hverdags mocking og test.

Vi laver muligvis 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.

6.1. Artikler i serien

Alle artikler i serien:

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