Testing av en abstrakt klasse med JUnit

1. Oversikt

I denne opplæringen vil vi analysere ulike brukssaker og mulige alternative løsninger for enhetstesting av abstrakte klasser med ikke-abstrakte metoder.

Noter det testing av abstrakte klasser bør nesten alltid gå gjennom det offentlige API-et for de konkrete implementeringene, så ikke bruk teknikkene nedenfor med mindre du er sikker på hva du gjør.

2. Maven-avhengigheter

La oss starte med Maven-avhengigheter:

 org.junit.jupiter junit-jupiter-engine 5.1.0 test org.mockito mockito-core 2.8.9 test org.powermock powermock-module-junit4 1.7.4 test junit junit org.powermock powermock-api-mockito2 1.7.4 test 

Du finner de nyeste versjonene av disse bibliotekene på Maven Central.

Powermock støttes ikke fullt ut for Junit5. Også, powermock-module-junit4 brukes bare til ett eksempel presentert i avsnitt 5.

3. Uavhengig ikke-abstrakt metode

La oss vurdere et tilfelle når vi har en abstrakt klasse med en offentlig ikke-abstrakt metode:

offentlig abstrakt klasse AbstraktUavhengig {offentlig abstrakt int abstraktFunc (); public String defaultImpl () {return "DEFAULT-1"; }}

Vi ønsker å teste metoden defaultImpl (), og vi har to mulige løsninger - ved å bruke en konkret klasse eller bruke Mockito.

3.1. Bruke en betongklasse

Lag en konkret klasse som strekker seg Abstrakt Uavhengig klasse, og bruk den til å teste metoden:

offentlig klasse ConcreteImpl utvider AbstractIndependent {@Override public int abstractFunc () {retur 4; }}
@Test public void givenNonAbstractMethod_whenConcreteImpl_testCorrectBehaviour () {ConcreteImpl conClass = new ConcreteImpl (); String faktisk = conClass.defaultImpl (); assertEquals ("DEFAULT-1", faktisk); }

Ulempen med denne løsningen er behovet for å lage den konkrete klassen med dummyimplementeringer av alle abstrakte metoder.

3.2. Bruke Mockito

Alternativt kan vi bruke Mockito for å lage en hån:

@Test offentlig ugyldig gittNonAbstractMethod_whenMockitoMock_testCorrectBehaviour () {AbstractIndependent absCls = Mockito.mock (AbstractIndependent.class, Mockito.CALLS_REAL_METHODS); assertEquals ("DEFAULT-1", absCls.defaultImpl ()); }

Den viktigste delen her er forberedelse av mock for å bruke den virkelige koden når en metode påberopes ved hjelp av Mockito.CALLS_REAL_METHODS.

4. Abstrakt metode kalt fra ikke-abstrakt metode

I dette tilfellet definerer den ikke-abstrakte metoden den globale utførelsesflyten, mens den abstrakte metoden kan skrives på forskjellige måter, avhengig av brukssaken:

offentlig abstrakt klasse AbstractMethodCalling {offentlig abstrakt String abstractFunc (); public String defaultImpl () {String res = abstractFunc (); returnere (res == null)? "Standard": (res + "Standard"); }}

For å teste denne koden kan vi bruke de samme to tilnærmingene som før - enten lage en konkret klasse eller bruke Mockito til å lage en mock:

@Test offentlig ugyldighet gittDefaultImpl_whenMockAbstractFunc_thenExpectedBehaviour () {AbstractMethodCalling cls = Mockito.mock (AbstractMethodCalling.class); Mockito.when (cls.abstractFunc ()) .thenReturn ("Abstract"); Mockito.doCallRealMethod () .when (cls) .defaultImpl (); assertEquals ("Abstrakt standard", cls.defaultImpl ()); }

Her, den abstractFunc () er stubbet med den returverdien vi foretrekker for testen. Dette betyr at når vi kaller den ikke-abstrakte metoden defaultImpl (), den vil bruke denne stubben.

5. Ikke-abstrakt metode med testhindring

I noen scenarier kaller metoden vi ønsker å teste en privat metode som inneholder en testhindring.

Vi må omgå den hindrende testmetoden før vi tester målmetoden:

offentlig abstrakt klasse AbstractPrivateMethods {offentlig abstrakt int abstraktFunc (); public String defaultImpl () {return getCurrentDateTime () + "DEFAULT-1"; } privat streng getCurrentDateTime () {return LocalDateTime.now (). toString (); }}

I dette eksemplet er defaultImpl () metoden kaller den private metoden getCurrentDateTime (). Denne private metoden får den aktuelle tiden ved kjøretid, noe som bør unngås i enhetstestene våre.

Nå, for å spotte standardoppførselen til denne private metoden, kan vi ikke engang bruke Mockito fordi den ikke kan kontrollere private metoder.

I stedet må vi bruke PowerMock (nVær oppmerksom på at dette eksemplet bare fungerer med JUnit 4 fordi støtte for denne avhengigheten ikke er tilgjengelig for JUnit 5):

@RunWith (PowerMockRunner.class) @PrepareForTest (AbstractPrivateMethods.class) public class AbstractPrivateMethodsUnitTest {@Test public void whenMockPrivateMethod_thenVerifyBehaviour () {AbstractPrivateMethods mockClass = PowerMockito.mock; PowerMockito.doCallRealMethod () .when (mockClass) .defaultImpl (); String dateTime = LocalDateTime.now (). ToString (); PowerMockito.doReturn (dateTime) .when (mockClass, "getCurrentDateTime"); String faktisk = mockClass.defaultImpl (); assertEquals (dateTime + "DEFAULT-1", faktisk); }}

Viktige biter i dette eksemplet:

  • @RunWith definerer PowerMock som løper for testen
  • @PrepareForTest (klasse) ber PowerMock om å forberede klassen for senere behandling

Interessant, vi spør PowerMock å stubbe den private metoden getCurrentDateTime (). PowerMock vil bruke refleksjon for å finne den fordi den ikke er tilgjengelig utenfra.

, når vi ringer defaultImpl (), blir stubben som er opprettet for en privat metode, påkalt i stedet for den faktiske metoden.

6. Ikke-abstrakt metode som får tilgang til forekomstfelt

Abstrakte klasser kan ha en intern tilstand implementert med klassefelt. Verdien av feltene kan ha en betydelig innvirkning på metoden som blir testet.

Hvis et felt er offentlig eller beskyttet, kan vi enkelt få tilgang til det fra testmetoden.

Men hvis det er privat, må vi bruke det PowerMockito:

offentlig abstrakt klasse AbstractInstanceFields {beskyttet int count; privat boolsk aktiv = falsk; offentlig abstrakt int abstraktFunc (); public String testFunc () {if (count> 5) {return "Overflow"; } gå tilbake aktiv? "Lagt til": "Blokkert"; }}

Her, den testFunc () metoden bruker felt på forekomstnivå telle og aktiv før den kommer tilbake.

Ved testing testFunc (), kan vi endre verdien på telle felt ved å få tilgang til forekomst opprettet ved hjelp av Mockito.

På den annen side for å teste oppførselen med det private aktiv feltet, må vi igjen bruke PowerMockito, og dets Whitebox klasse:

@Test offentlig ugyldig nårPowerMockitoAndActiveFieldTrue_thenCorrectBehaviour () {AbstractInstanceFields instClass = PowerMockito.mock (AbstractInstanceFields.class); PowerMockito.doCallRealMethod () .when (instClass) .tFFunc (); Whitebox.setInternalState (instClass, "aktiv", sant); assertEquals ("Lagt til", instClass.testFunc ()); }

Vi lager en stubbklasse ved hjelp av PowerMockito.mock (), og vi bruker Whitebox klasse for å kontrollere objektets interne tilstand.

Verdien av aktiv felt endres til ekte.

7. Konklusjon

I denne opplæringen har vi sett flere eksempler som dekker mange brukssaker. Vi kan bruke abstrakte klasser i mange flere scenarier, avhengig av designet som følges.

Dessuten er det å skrive enhetstester for abstrakte klassemetoder like viktig som for normale klasser og metoder. Vi kan teste hver av dem ved hjelp av forskjellige teknikker eller forskjellige tilgjengelige testbiblioteker.

Hele kildekoden er tilgjengelig på GitHub.


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