Programmering

JUnit 5 tutorial, del 1: Enhetstesting med JUnit 5, Mockito og Hamcrest

JUnit 5 er den nye de facto-standarden for utvikling av enhetstester i Java. Denne nyeste versjonen har etterlatt begrensningene til Java 5 og integrert mange funksjoner fra Java 8, spesielt støtte for lambda-uttrykk.

I denne første halvdelen av en todelt introduksjon til JUnit 5 kommer du i gang med testing med JUnit 5. Jeg viser deg hvordan du konfigurerer et Maven-prosjekt til å bruke JUnit 5, hvordan du skriver tester ved hjelp av @Test og @ParameterizedTest merknader, og hvordan du arbeider med de nye livssykluskommentarene i JUnit 5. Du vil også se et kort eksempel på bruk av filteretiketter, og jeg viser deg hvordan du integrerer JUnit 5 med et påstandsbibliotek fra tredjepart - i dette tilfellet , Hamcrest. Til slutt får du en rask, opplæringsinnledning til å integrere JUnit 5 med Mockito, slik at du kan skrive mer robuste enhetstester for komplekse, virkelige systemer.

last ned Få koden Få kildekoden for eksempler i denne veiledningen. Skapt av Steven Haines for JavaWorld.

Testdrevet utvikling

Hvis du har utviklet Java-kode i en periode, er du sannsynligvis godt kjent med testdrevet utvikling, så jeg vil holde denne delen kort. Det er viktig å forstå Hvorfor vi skriver enhetstester, men også strategiene utviklere bruker når de designer enhetstester.

Testdrevet utvikling (TDD) er en programvareutviklingsprosess som fletter inn koding, testing og design. Det er en test-første tilnærming som tar sikte på å forbedre kvaliteten på applikasjonene dine. Testdrevet utvikling er definert av følgende livssyklus:

  1. Legg til en test.
  2. Kjør alle testene dine og følg den nye testen som mislykkes.
  3. Implementere koden.
  4. Kjør alle testene og følg den nye testen som følger.
  5. Refaktorere koden.

Figur 1 viser denne TDD-livssyklusen.

Steven Haines

Det er et dobbelt formål med å skrive tester før du skriver koden din. For det første tvinger det deg til å tenke på forretningsproblemet du prøver å løse. Hvordan skal for eksempel vellykkede scenarier oppføre seg? Hvilke forhold skal mislykkes? Hvordan skal de mislykkes? For det andre gir testing deg mer tillit til testene dine. Når jeg skriver tester etter å ha skrevet kode, må jeg alltid bryte dem for å sikre at de faktisk får feil. Skrivetester unngår først dette ekstra trinnet.

Å skrive tester for den lykkelige veien er vanligvis lett: Gitt gode innspill, skal klassen gi et deterministisk svar. Men å skrive negative (eller mislykkede) testtilfeller, spesielt for komplekse komponenter, kan være mer komplisert.

Tenk som eksempel på å skrive tester for et databaselager. På den lykkelige banen setter vi inn en post i databasen og mottar tilbake det opprettede objektet, inkludert genererte nøkler. I virkeligheten må vi også vurdere muligheten for en konflikt, for eksempel å sette inn en post med en unik kolonneverdi som allerede er holdt av en annen post. I tillegg, hva skjer når depotet ikke kan koble til databasen, kanskje fordi brukernavnet eller passordet har endret seg? Hva skjer hvis det er en nettverksfeil under transport? Hva skjer hvis forespørselen ikke fullføres i den definerte tidsavgrensningen?

For å bygge en robust komponent må du vurdere alle sannsynlige og usannsynlige scenarier, utvikle tester for dem og skrive koden din for å tilfredsstille disse testene. Senere i artikkelen vil vi se på strategier for å lage forskjellige feilscenarier, sammen med noen av de nye funksjonene i JUnit 5 som kan hjelpe deg med å teste disse scenariene.

Vedta JUnit 5

Hvis du har brukt JUnit en stund, vil noen av endringene i JUnit 5 være en justering. Her er et sammendrag på høyt nivå av hva som er forskjellig mellom de to versjonene:

  • JUnit 5 er nå pakket i org.junit.jupiter gruppe, som endrer hvordan du vil inkludere den i Maven og Gradle-prosjektene.
  • JUnit 4 krevde et minimum JDK på JDK 5; JUnit 5 krever minimum JDK 8.
  • JUnit 4 @Før, @BeforeClass, @Etter, og @Etter timen merknader er erstattet av @BeforeEach, @BeforeAll, @EfterEach, og @Tross alt, henholdsvis.
  • JUnit 4-er @Overse merknaden er erstattet av @Funksjonshemmet kommentar.
  • De @Kategori merknaden er erstattet av @Stikkord kommentar.
  • JUnit 5 legger til et nytt sett med påstandsmetoder.
  • Løpere har blitt erstattet med utvidelser, med et nytt API for utvidelsesimplementører.
  • JUnit 5 introduserer antagelser som hindrer at en test utføres.
  • JUnit 5 støtter nestede og dynamiske testklasser.

Vi vil utforske de fleste av disse nye funksjonene i denne artikkelen.

Enhetstesting med JUnit 5

La oss starte enkelt, med et ende-til-slutt-eksempel på å konfigurere et prosjekt for å bruke JUnit 5 til en enhetstest. Oppføring 1 viser a MathTools klasse hvis metode konverterer en teller og en nevner til en dobbelt.

Oppføring 1. Et eksempel på et JUnit 5-prosjekt (MathTools.java)

 pakke com.javaworld.geekcap.math; offentlig klasse MathTools {offentlig statisk dobbel convertToDecimal (int teller, int nevner) {if (nevner == 0) {kast ny IllegalArgumentException ("Nevner må ikke være 0"); } returner (dobbel) teller / (dobbel) nevner; }}

Vi har to primære scenarier for testing av MathTools klasse og dens metode:

  • EN gyldig test, der vi sender ikke-null heltall for teller og nevner.
  • EN feilscenario, der vi sender en nullverdi for nevneren.

Oppføring 2 viser en JUnit 5-testklasse for å teste disse to scenariene.

Oppføring 2. En JUnit 5 testklasse (MathToolsTest.java)

 pakke com.javaworld.geekcap.math; importere java.lang.IllegalArgumentException; importer org.junit.jupiter.api.Assertions; importer org.junit.jupiter.api.Test; klasse MathToolsTest {@Test ugyldig testConvertToDecimalSuccess () {dobbelt resultat = MathTools.convertToDecimal (3, 4); Assertions.assertEquals (0,75, resultat); } @Test ugyldig testConvertToDecimalInvalidDenominator () {Assertions.assertThrows (IllegalArgumentException.class, () -> MathTools.convertToDecimal (3, 0)); }}

I Listing 2, the testConvertToDecimalInvalidDenominator metoden utfører MathTools :: convertToDecimal metode inne i en hevder Kaster anrop. Det første argumentet er den forventede typen unntak som skal kastes. Det andre argumentet er en funksjon som vil kaste det unntaket. De hevder Kaster metoden utfører funksjonen og validerer at den forventede typen unntak kastes.

Assertions-klassen og dens metoder

Deorg.junit.jupiter.api.Test kommentar betegner en testmetode. Merk at @Test merknader kommer nå fra JUnit 5 Jupiter API-pakken i stedet for JUnit 4 org.junit pakke. De testConvertToDecimalSuccess metoden først utfører MathTools :: convertToDecimal metoden med en teller på 3 og en nevner på 4, hevder deretter at resultatet er lik 0,75. De org.junit.jupiter.api.Assertions klasse gir et sett med statisk metoder for å sammenligne faktiske og forventede resultater. De Påstander klasse har følgende metoder, som dekker de fleste av de primitive datatypene:

  • assertArrayEquals sammenligner innholdet i en faktisk matrise med en forventet matrise.
  • hevderEquals sammenligner en faktisk verdi med en forventet verdi.
  • assertNotEquals sammenligner to verdier for å validere at de ikke er like.
  • hevder sant validerer at den oppgitte verdien er sann.
  • hevder falske validerer at den oppgitte verdien er falsk.
  • assertLinesMatch sammenligner to lister over Strings.
  • hevder Null validerer at den oppgitte verdien er null.
  • hevderNotNull validerer at den oppgitte verdien ikke er null.
  • hevderSamme validerer at to verdier refererer til det samme objektet.
  • assertNotSame validerer at to verdier ikke refererer til det samme objektet.
  • hevder Kaster validerer at utførelsen av en metode gir et forventet unntak (du kan se dette i testConvertToDecimalInvalidDenominator eksempel ovenfor).
  • assertTimeout validerer at en levert funksjon fullføres innen en spesifisert tidsavbrudd
  • assertTimeoutForebyggende validerer at en levert funksjon fullføres innen et spesifisert tidsavbrudd, men når tidsavbruddet er nådd, dreper den funksjonen.

Hvis noen av disse påstandsmetodene mislykkes, merkes enhetstesten som mislykket. Denne feilmeldingen vil bli skrevet til skjermen når du kjører testen, og deretter lagret i en rapportfil.

Bruke delta med assertEquals

Når du bruker flyte og dobbelt verdier i en hevderEquals, kan du også spesifisere en delta som representerer en terskel for forskjell mellom de to. I vårt eksempel kunne vi ha lagt til et delta på 0,001, i tilfelle 0,75 faktisk ble returnert som 0,750001.

Analyserer testresultatene

I tillegg til å validere en verdi eller atferd, har hevder metoder kan også godta en tekstbeskrivelse av feilen, som kan hjelpe deg med å diagnostisere feil. For eksempel:

 Assertions.assertEquals (0,75, resultat, "MathTools :: convertToDecimal-verdien returnerte ikke riktig verdi på 0,75 for 3/4"); Assertions.assertEquals (0,75, resultat, () -> "MathTools :: convertToDecimal-verdien returnerte ikke den riktige verdien på 0,75 for 3/4"); 

Utgangen viser den forventede verdien på 0,75 og den faktiske verdien. Den vil også vise den angitte meldingen, som kan hjelpe deg med å forstå feilkonteksten. Forskjellen mellom de to variasjonene er at den første alltid lager meldingen, selv om den ikke vises, mens den andre bare konstruerer meldingen hvis påstanden mislykkes. I dette tilfellet er konstruksjonen av meldingen triviell, så det spiller ingen rolle. Det er likevel ikke behov for å konstruere en feilmelding for en test som går, så det er vanligvis en god praksis å bruke den andre stilen.

Til slutt, hvis du bruker en IDE som IntelliJ til å kjøre testene dine, vil hver testmetode vises med metodens navn. Dette er greit hvis metodenavnene dine er lesbare, men du kan også legge til et @DisplayName kommentar til testmetodene dine for bedre å identifisere testene:

@Test @DisplayName ("Test vellykket desimalkonvertering") ugyldig testConvertToDecimalSuccess () {dobbelt resultat = MathTools.convertToDecimal (3, 4); Assertions.assertEquals (0,751, resultat); }

Kjører enhetstesten

For å kjøre JUnit 5-tester fra et Maven-prosjekt, må du inkludere maven-surefire-plugin i Maven pom.xml fil og legg til en ny avhengighet. Listing 3 viser pom.xml fil for dette prosjektet.

Oppføring 3. Maven pom.xml for et eksempel på JUnit 5-prosjekt

  4.0.0 com.javaworld.geekcap junit5 jar 1.0-SNAPSHOT org.apache.maven.plugins maven-compiler-plugin 3.8.1 8 8 org.apache.maven.plugins maven-surefire-plugin 3.0.0-M4 junit5 // maven.apache.org org.junit.jupiter junit-jupiter 5.6.0 test 

JUnit 5 avhengigheter

JUnit 5 pakker komponentene i org.junit.jupiter gruppe, og vi må legge til junit-jupiter artifact, som er en aggregatorgjenstand som importerer følgende avhengigheter:

  • junit-jupiter-api definerer API for å skrive tester og utvidelser.
  • junit-jupiter-motor er implementeringen av testmotoren som kjører enhetstestene.
  • junit-jupiter-params gir støtte for parametriserte tester.

Deretter må vi legge til maven-surefire-plugin bygge plug-in for å kunne kjøre testene.

Til slutt må du ta med maven-compiler-plugin med en versjon av Java 8 eller nyere, slik at du kan bruke Java 8-funksjoner som lambdas.

Kjør det!

Bruk følgende kommando for å kjøre testklassen fra IDE eller fra Maven:

mvn ren test

Hvis du lykkes, bør du se utdata som ligner på følgende:

 [INFO] ------------------------------------------------------- -------- [INFO] TESTER [INFO] ----------------------------------- -------------------- [INFO] Kjører com.javaworld.geekcap.math.MathToolsTest [INFO] Testkjøring: 2, Feil: 0, Feil: 0, hoppet over : 0, forløpt tid: 0,04 s - i com.javaworld.geekcap.math.MathToolsTest [INFO] [INFO] Resultater: [INFO] [INFO] Testkjøring: 2, Feil: 0, Feil: 0, Hoppet over: 0 [ INFO] [INFO] --------------------------------------------- --------------------------- [INFO] BYGG SUKSESS [INFO] --------------- -------------------------------------------------- ------- [INFO] Total tid: 3.832 s [INFO] Ferdig kl: 2020-02-16T08: 21: 15-05: 00 [INFO] ------------- -------------------------------------------------- --------- 
$config[zx-auto] not found$config[zx-overlay] not found