JMockit avansert bruk

1. Introduksjon

I denne artikkelen vil vi gå utover grunnleggende om JMockit, og vi begynner å se på noen avanserte scenarier, for eksempel:

  • Falske (eller MockUp API)
  • De Avkapsling bruksklasse
  • Hvordan spotte mer enn ett grensesnitt med bare en mock
  • Hvordan gjenbruke forventninger og verifikasjoner

Hvis du vil oppdage grunnleggende om JMockit, kan du sjekke andre artikler fra denne serien. Du finner relevante lenker nederst på siden.

2. Maven avhengighet

Først må vi legge til jmockit-avhengigheten til prosjektet vårt:

 org.jmockit jmockit 1.41 

Deretter fortsetter vi med eksemplene.

3. Private metoder / Mocking av indre klasser

Spott og testing av private metoder eller indre klasser anses ofte ikke som god praksis.

Begrunnelsen bak det er at hvis de er private, bør de ikke testes direkte, da de er klassens innerste tarm, men noen ganger må det fortsatt gjøres, spesielt når det gjelder eldre kode.

Med JMockit har du to alternativer for å håndtere disse:

  • De MockUp API for å endre den virkelige implementeringen (for det andre tilfellet)
  • De Avkapsling utility class, for å ringe hvilken som helst metode direkte (for det første tilfellet)

Alle følgende eksempler vil bli gjort for følgende klasse, og vi antar at de kjøres på en testklasse med samme konfigurasjon som den første (for å unngå å gjenta kode):

offentlig klasse AdvancedCollaborator {int i; private int privateField = 5; // standardkonstruktør utelatt offentlig AdvancedCollaborator (strengstreng) kaster unntak {i = streng.lengde (); } public String methodThatCallsPrivateMethod (int i) {return privateMethod () + i; } public int methodThatReturnsThePrivateField () {return privateField; } private String privateMethod () {return "standard:"; } klasse InnerAdvancedCollaborator {...}}

3.1. Faking With MockUp

JMockits Mockup API gir støtte for opprettelse av falske implementeringer eller mock-ups. Vanligvis er en mock-up retter seg mot noen få metoder og / eller konstruktører i klassen som skal forfalskes, mens de fleste andre metoder og konstruktører blir umodifisert. Dette muliggjør en fullstendig omskrivning av en klasse, slik at enhver metode eller konstruktør (med hvilken som helst tilgangsmodifikator) kan målrettes.

La oss se hvordan vi kan definere på nytt privateMethod () ved hjelp av Mockups API:

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

I dette eksemplet definerer vi et nytt MockUp for AdvancedCollaborator klasse ved hjelp av @Håne kommentar på en metode med matchende signatur. Etter dette vil samtaler til den metoden bli delegert til vår hånede.

Vi kan også bruke dette til mock-up konstruktøren av en klasse som trenger spesifikke argumenter eller konfigurasjon for å forenkle tester:

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

I dette eksemplet kan vi se at for konstruktørspott må du spotte $ init metode. Du kan sende et ekstra argument av typen Påkallelse, som du kan få tilgang til informasjon om påkallelse av den spottede metoden, inkludert forekomsten som påkallingen utføres for.

3.2. Bruker Avkapsling Klasse

JMockit inkluderer en testverktøysklasse: Avkapsling. Som navnet antyder, brukes den til å kapsle inn en tilstand av et objekt, og ved å bruke den kan du forenkle testingen ved å få tilgang til felt og metoder som ellers ikke var tilgjengelige.

Du kan påkalle en metode:

@Test offentlig ugyldig testToCallPrivateMethodsDirectly () {Object value = Deencapsulation.invoke (mock, "privateMethod"); assertEquals ("standard:", verdi); }

Du kan også angi felt:

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

Og få felt:

@Test offentlig ugyldig testToGetPrivateFieldDirectly () {int value = Deencapsulation.getField (mock, "privateField"); assertEquals (5, verdi); }

Og opprett nye forekomster av klasser:

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

Selv nye forekomster av indre klasser:

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

Som du kan se, er Avkapsling klasse er ekstremt nyttig når man tester lufttette klasser. Et eksempel kan være å angi avhengigheter for en klasse som bruker @Autowired kommentarer på private felt og har ingen setter for dem, eller for å enhetsteste indre klasser uten å være avhengig av det offentlige grensesnittet til containerklassen.

4. Spotte flere grensesnitt i en og samme mock

La oss anta at du vil teste en klasse - ennå ikke implementert - men du vet helt sikkert at den vil implementere flere grensesnitt.

Vanligvis vil du ikke kunne teste klassen før du implementerer den, men med JMockit har du muligheten til å forberede tester på forhånd ved å spotte mer enn ett grensesnitt ved hjelp av ett mock-objekt.

Dette kan oppnås ved å bruke generiske produkter og definere en type som utvider flere grensesnitt. Denne generiske typen kan enten defineres for en hel testklasse eller for bare en testmetode.

For eksempel skal vi lage en mock for grensesnitt Liste og Sammenlignelig to veier:

@RunWith (JMockit.class) offentlig klasse AdvancedCollaboratorTest> {@Mocked privat MultiMock multiMock; @Test offentlig ugyldig testOnClass () {new Expectations () {{multiMock.get (5); resultat = "foo"; multiMock.compareTo ((List) any); resultat = 0; }}; assertEquals ("foo", multiMock.get (5)); assertEquals (0, multiMock.compareTo (ny ArrayList ())); } @Test offentlig > 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 (new ArrayList ())); }}

Som du kan se i linje 2, kan vi definere en ny testtype for hele testen ved å bruke generiske på klassenavnet. Den veien, MultiMock vil være tilgjengelig som en type, og du vil kunne lage mocks for den ved hjelp av noen av JMockits merknader.

I linjene 7 til 18 kan vi se et eksempel ved å bruke en mock av en flerklasse definert for hele testklassen.

Hvis du trenger multi-interface mock for bare en test, kan du oppnå dette ved å definere den generiske typen på metodesignaturen og sende en ny mock av den nye genericen som testmetode-argumentet. I linje 20 til 32 kan vi se et eksempel på å gjøre det for den samme testede atferden som i forrige test.

5. Gjenbruk av forventninger og bekreftelser

Til slutt, når du tester klasser, kan du støte på tilfeller der du gjentar det samme Forventninger og / eller Bekreftelser igjen og igjen. For å lette det kan du enkelt bruke begge deler.

Vi skal forklare det med et eksempel (vi bruker klassene Modell, samarbeidspartner, og Utøver fra vår JMockit 101-artikkel):

@RunWith (JMockit.class) offentlig klasse ReusingTest {@Injectable private Collaborator collaborator; @Mocked privat modellmodell; @ Testet privat utøver utøver; @Før offentlig ugyldig oppsett () {new Expectations () {{model.getInfo (); resultat = "foo"; minTimes = 0; collaborator.collaborate ("foo"); resultat = sant; minTimes = 0; }}; } @Test offentlig ugyldig testWithSetup () {performer.perform (modell); verifisereTrueCalls (1); } beskyttet ugyldig verifisereTrueCalls (int samtaler) {new Verifications () {{collaborator.receive (true); ganger = samtaler; }}; } siste klasse TrueCallsVerification utvider Bekreftelser {public TrueCallsVerification (int samtaler) {collaborator.receive (true); ganger = samtaler; }} @Test offentlig ugyldig testWithFinalClass () {performer.perform (modell); nye TrueCallsVerification (1); }}

I dette eksemplet kan du se i linjer fra 15 til 18 at vi forbereder en forventning for hver test slik at model.getInfo () kommer alltid tilbake “Foo” og for samarbeidspartner. samarbeide() å alltid forvente “Foo” som argumentet og retur ekte. Vi setter minTimes = 0 uttalelse slik at ingen feil vises når du ikke bruker dem i tester.

Vi har også laget metode verifisereTrueCalls (int) for å forenkle bekreftelser til samarbeidspartner. motta (boolsk) metoden når bestått argument er ekte.

Til slutt kan du også opprette nye typer spesifikke forventninger og bekreftelser bare utvide noen av Forventninger eller Bekreftelser klasser. Deretter definerer du en konstruktør hvis du trenger å konfigurere atferden og opprette en ny forekomst av nevnte type i en test som vi gjør i linjene 33 til 43.

6. Konklusjon

Med denne delen av JMockit-serien har vi berørt flere avanserte emner som definitivt vil hjelpe deg med hverdagsspott og testing.

Vi kan gjøre flere artikler om JMockit, så hold deg oppdatert for å lære enda mer.

Og som alltid kan den fulle implementeringen av denne opplæringen finnes på GitHub.

6.1. Artikler i serien

Alle artiklene i serien:

  • JMockit 101
  • En guide til JMockit forventninger
  • JMockit avansert bruk

$config[zx-auto] not found$config[zx-overlay] not found