CodeGym /Java-blogg /Tilfeldig /Enhetstesting i Java med JUnit
John Squirrels
Nivå
San Francisco

Enhetstesting i Java med JUnit

Publisert i gruppen

Hva er enhetstesting i Java?

Før vi begynner å lære JUnit i Java, la oss en kort oversikt over hva enhetstesting er og hvorfor det er så populært (hvis du kan dette allerede, hopp til 'Hvordan skriver jeg en JUnit-test i Java?'). Enhetstesting i Java gjør storskala programvareutvikling mye mer effektiv og uanstrengt. Det kan hjelpe både enkeltpersoner og team å kutte utallige timer med feilsøking og effektivisere samarbeidsprosessen enormt. Enhetstesting i Java med JUnit - 1

https://junit.org/junit4/

Den essensielle ideen med enhetstesting er denne: skriv atomtester av individuelle funksjoner (kalt enhetstester) og legg sakte til flere funksjoner etter testing og sørg for at de forrige fungerer. Det er en ekstremt enkel, men kraftig idé. Som et eksempel på hvordan denne prosessen kan se ut, forestill deg at du bygde en virtuell vitenskapelig kalkulator. I tillegg til de tilsynelatende aritmetiske operatorene ( +, -, x, %), vil denne kalkulatoren ha avanserte funksjoner som krever andre underfunksjoner for å fungere i den. For å beregne eksponenter må kalkulatoren din kunne multiplisere riktig. Så en enhetstesting tilnærming til å bygge og teste denne kalkulatoren vil være:
  • Skriv en tilleggsfunksjon. Test det nøye, bytt det, gjenta til det fungerer.
  • Gjør det samme for subtraksjon, multiplikasjon, divisjonsfunksjoner.
  • Bruk disse baseoperatorene til å skrive mer avanserte operatorfunksjoner som eksponenter, og test deretter disse funksjonene også.
Dette sikrer at funksjoner som bygger på andre mindre underfunksjoner, ikke bare fungerer som de skal i seg selv, men at de ikke har defekte underfunksjoner. For eksempel, hvis jeg tester eksponentfunksjonen og noe går galt, vet jeg at feilen sannsynligvis ikke er i multiplikasjonsunderfunksjonen, fordi multiplikasjonsfunksjonen allerede ble grundig testet. Dette eliminerer i stor grad den totale mengden kode jeg trenger å spore tilbake og inspisere for å finne feilen. Forhåpentligvis gjør dette trivielle eksemplet klart hvordan tankeprosessen rundt Unit Testing er strukturert. Men hvordan samhandler enhetstesting med resten av programvareutviklingsprosessen? Hva om du har enda mer komplekse funksjoner, som må kunne fungere og kommunisere sammen? Enhetstesting er utilstrekkelig for å sikre at slike komplekse funksjoner kan fungere ordentlig sammen. Faktisk er det bare det første trinnet i de fire nivåene for programvaretesting (jeg bruker store bokstaver fordi jeg refererer til industristandarden eller den vanligste tilnærmingen til å teste programvare). De tre siste trinnene erIntegrasjonstesting , Systemtesting og Aksepttesting. Disse betyr sannsynligvis akkurat det du tror de gjør, men la meg presisere: Integrasjonstesting er det vi ville gjort for å sikre at de som nevnt ovenfor, "komplekse funksjoner", samhandler på riktig måte med hverandre. (f.eks. sørge for at kalkulatoren kan håndtere "3 + 7 * 4 - 2") Systemtesting er å teste den generelle designen til et bestemt system; det er ofte flere systemer med komplekse funksjoner som jobber sammen i et produkt, så du grupperer disse i systemer og tester dem individuelt. (hvis du f.eks. bygde en grafisk kalkulator, ville du først bygge det aritmetiske 'systemet' for å håndtere tall, teste til det fungerer etter hensikten, og deretter bygge og teste det grafiske 'systemet' for å håndtere uttak, som det ville bygge av det aritmetiske systemet). Aksepttesting er testing på brukernivå; det er å se om alle systemer kan fungere synkronisert for å lage et ferdig produkt som er klart til å bli akseptert av brukere (f.eks. brukere som tester kalkulatoren). Programvareutviklere kan noen ganger ignorere dette siste trinnet i prosessen, ettersom selskaper ofte vil ha andre ansatte til å distribuere brukertester (beta) separat.

Hvordan skriver jeg en JUnit-test i Java?

Nå som du har en klarere ide om fordelene og begrensningene ved enhetstesting, la oss ta en titt på litt kode! Vi kommer til å bruke et populært Java-testrammeverk kalt JUnit (en annen populær er TestNG, som du også kan bruke hvis du vil. De er veldig like syntaktisk; TestNG er inspirert av JUnit). Du kan laste ned og installere JUnit her . For denne eksempelkoden vil vi fortsette med "vitenskapelig kalkulator"-eksemplet jeg nevnte tidligere; det er ganske enkelt å vikle hodet rundt, og testkoden er superenkel. Konvensjonell praksis er å skrive separate testklasser for hver av klassene dine, så det er det vi skal gjøre. La oss anta at vi på dette tidspunktet har en Math.javafil med alle matematiske funksjoner i den (inkludert Math.add), og vi skriver enMathTests.javafil i samme pakke. La oss nå sette opp importsetninger og klassetekst: (MULIG JUnit INTERVJU SPØRSMÅL: Du kan bli spurt om hvor du skal plassere JUnit-testen din og om du trenger å importere kildefilene dine. Hvis du skriver testklassene dine i samme pakke som hovedklassene dine, trenger du ingen importsetninger for kildefilene dine i testklassen. Ellers må du sørge for at du importerer kildefilene dine!)

import org.junit.jupiter.Test;    //gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; //less typing :) 

public class MathTests {
	//...
}
Den første importsetningen gir oss @Testoverskriften. Vi skriver ' @Test' direkte på toppen av hver testfunksjonsdefinisjon, slik at JUnit vet at dette er en enkelt enhetstest som kan kjøres separat. Senere vil jeg vise deg hvordan du kan kjøre spesifikke enhetstester ved å bruke denne overskriften. Den andre importerklæringen sparer oss for litt skriving. Den primære JUnit-funksjonen vi bruker for å teste funksjonene våre er å Assert.assertEquals(), som tar to parametere (faktisk verdi og forventet verdi) og sørger for at de er like. Ved å ha denne andre importsetningen kan vi bare skrive ' assertEquals(...' i stedet for å måtte spesifisere hver gang hvilken pakke den er en del av. La oss nå skrive en veldig enkel testcase for å bekrefte at 2 + 2 faktisk er 4!

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test
	public void add_twoPlusTwo_returnsFour(){
	final int expected = 4;
	final int actual = Math.add(2, 2);
	assertEquals(“2+2 is 4”, actual, expected);
	}
}
La oss gå over hver av testfunksjonens fem linjer og hva de gjør: Linje 5: Denne @Testoverskriften spesifiserer at funksjonsdefinisjonen nedenfor add_twoPlusTwo_returnsFour()faktisk er en testfunksjon som JUnit kan kjøre separat. Linje 6: Dette er funksjonssignaturen for vår testcase. Testtilfeller er alltid svært enkeltstående; de tester bare ett spesifikt eksempel, for eksempel 2+2=4. Det er vanlig å navngi testtilfellene dine i formen " [function]_[params]_returns[expected]()," hvor [function]er navnet på funksjonen du tester, [params]er de spesifikke eksempelparametrene du tester, og [expected]er den forventede returverdien til funksjonen. Testfunksjoner har nesten alltid returtypen ' void' fordi hovedpoenget med hele funksjonen er å kjøreassertEquals, som vil sende ut til konsollen om testen bestått eller ikke; du trenger ikke andre data for å bli returnert noe sted. Linje 7: Vi erklærer en ' final' variabel av returtypen til Math.add (int), og gir den navnet 'forventet' etter konvensjon. Dens verdi er svaret vi forventer (4). Linje 8: Vi erklærer en ' final' variabel av returtypen til Math.add (int), og kaller den 'faktisk' etter konvensjon. Verdien er resultatet av Math.add(2, 2). Linje 9: Den gylne streken. Dette er linjen som sammenligner faktisk og forventet og forteller oss at vi bestod testen bare hvis de er like. Den første parameteren som sendes "2+2 er 4" er en beskrivelse av testfunksjonen.

Hva om funksjonen min skulle gi et unntak?

Hvis ditt spesifikke testeksempel skulle gi et unntak i stedet for å hevde at en faktisk og forventet verdi er like, så har JUnit en måte å klargjøre dette i overskriften @Test. La oss ta en titt på et eksempel nedenfor. Forutsatt at vi har en funksjon i Math.javakalt Math.divide, vil vi sørge for at innganger ikke kan deles på 0. I stedet Math.divide(a, 0)bør det å prøve å kalle for en 'a'-verdi gi et unntak ( ArithmeticException.class). Vi spesifiserer det i overskriften som sådan:

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test (expectedExceptions = ArithmeticException.class)
	public void divide_byZero_throwsException() throws ArithmeticException{
	Math.divide(1, 0);
	}
}
Du kan ha mer enn ett unntak for expectedExceptions, bare sørg for å bruke parenteser og kommaer for å liste opp unntaksklassene dine, som sådan:

expectedException = {FirstException.class, SecondException.class, … }

Hvordan kjører jeg JUnit-testene mine i Java?

Slik legger du til JUnit til IntelliJ: https://stackoverflow.com/questions/19330832/setting-up-junit-with-intellij-idea Du kan kjøre prosjektet ditt slik du vanligvis ville kjørt testene. Å kjøre alle testene i en testklasse vil kjøre dem i alfabetisk rekkefølge. I JUnit 5 kan du legge til en prioritet til testene ved å legge til en @Ordertag. Et eksempel:

@TestMethodOrder(OrderAnnotation.class)
public class Tests {
…
@Test
@Order(2)
public void a_test() { … }

@Test
@Order (1)
public void b_test() { … }
…
}
Selv om a_test()kommer foran b_test()alfabetisk og i koden, b_test()vil kjøre før a_test()her, fordi 1 kommer før 2 i rekkefølge. Så det er omtrent alt for det grunnleggende i JUnit. La oss nå takle et par vanlige JUnit-intervjuspørsmål, og lære litt mer om JUnit underveis!

JUnit-intervjuspørsmål (tilleggsinformasjon)

Her har jeg samlet de mest populære JUnit-intervjuspørsmålene. Hvis du har noe å legge til - gjør dette gjerne i kommentarene nedenfor. Spørsmål: Hvilken metode kan du kalle inn testmetoden din for automatisk å mislykkes i en test? A: fail(“feilbeskrivelse her!”); Spørsmål: Du tester en hundeklasse; for å teste et hundeobjekt, må du instansiere det før du kan kjøre tester på det. Så du skriver en setUp()-funksjon for å instansiere hunden. Du vil bare kjøre denne funksjonen én gang under hele testingen. Hva må du sette rett over setUp()-funksjonssignaturen slik at JUnit vet å kjøre setUp() før du kjører testene? A: @BeforeClass (@BeforeAll i JUnit 5) Q:Hva må være funksjonssignaturen til setUp()-funksjonen beskrevet ovenfor? A: offentlig statisk tomrom. Enhver funksjon med @BeforeClass (@BeforeAll i JUnit 5) eller @AfterClass (@AfterAll i JUnit 5) må være statiske. Spørsmål: Du er ferdig med å teste hundeklassen. Du skriver void tearDown() funksjon som rydder opp data og skriver ut informasjon til konsollen etter hver test. Du vil at denne funksjonen skal kjøre etter hver eneste test. Hva må du sette rett over tearDown()-funksjonssignaturen slik at JUnit vet å kjøre tearDown() etter å ha kjørt hver test? A: @After (@AfterEach i JUnit 5)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION