CodeGym /Java blog /Tilfældig /Enhedstest i Java med JUnit
John Squirrels
Niveau
San Francisco

Enhedstest i Java med JUnit

Udgivet i gruppen

Hvad er enhedstest i Java?

Inden vi begynder at lære JUnit i Java, lad os kort gennemgå, hvad enhedstest er, og hvorfor det er så populært (hvis du allerede ved dette, skal du springe til 'Hvordan skriver jeg en JUnit-test i Java?'). Enhedstest i Java gør softwareudvikling i stor skala meget mere effektiv og ubesværet. Det kan hjælpe både enkeltpersoner og teams med at skære utallige timer af med fejlretning og strømline samarbejdsprocessen enormt. Enhedstest i Java med JUnit - 1

https://junit.org/junit4/

Den essentielle idé med enhedstestning er denne: skriv atomprøver af individuelle funktioner (kaldet enhedstests) og tilføj langsomt flere funktioner efter test og sørg for, at de tidligere fungerer. Det er en ekstremt enkel, men kraftfuld idé. Som et eksempel på, hvordan denne proces kan se ud, forestil dig, at du byggede en virtuel videnskabelig lommeregner. Ud over de tilsyneladende aritmetiske operatorer ( +, -, x, %), ville denne lommeregner have avancerede funktioner, der kræver andre underfunktioner for at fungere i den. For at beregne eksponenter skal din lommeregner være i stand til at gange korrekt. Så en enhedstestmetode til at bygge og teste denne lommeregner ville være:
  • Skriv en tilføjelsesfunktion. Test det omhyggeligt, skift det, gentag indtil det virker.
  • Gør det samme for subtraktion, multiplikation, divisionsfunktioner.
  • Brug disse basisoperatorer til at skrive mere avancerede operatorfunktioner som eksponenter, og test derefter disse funktioner også.
Dette sikrer, at funktioner, der bygger på andre mindre underfunktioner, ikke kun fungerer korrekt i sig selv, men ikke har defekte underfunktioner i sig. For eksempel, hvis jeg tester eksponentfunktionen, og noget går galt, ved jeg, at fejlen sandsynligvis ikke er i multiplikationsunderfunktionen, fordi multiplikationsfunktionen allerede var grundigt testet. Dette fjerner i høj grad den samlede mængde kode, jeg skal spore og inspicere for at finde fejlen. Forhåbentlig gør dette trivielle eksempel klart, hvordan tankeprocessen omkring Unit Testing er struktureret. Men hvordan interagerer enhedstest med resten af ​​softwareudviklingsprocessen? Hvad hvis du har endnu mere komplekse funktioner, som skal kunne arbejde og kommunikere sammen? Enhedstestning er utilstrækkelig til at sikre, at sådanne komplekse funktioner kan fungere korrekt sammen. Faktisk er det kun det første trin i de fire niveauer af softwaretest (jeg bruger store bogstaver, fordi jeg henviser til industristandarden eller den mest almindelige tilgang til test af software). De sidste tre trin erIntegrationstest , systemtest og accepttest. Disse betyder alle sandsynligvis præcis, hvad du tror, ​​de gør, men lad mig præcisere: Integrationstest er, hvad vi ville gøre for at sikre, at de som nævnt ovenfor, "komplekse funktioner", interagerer korrekt med hinanden. (f.eks. at sikre, at lommeregneren kan håndtere "3 + 7 * 4 - 2") Systemtest er at teste det overordnede design af et bestemt system; der er ofte flere systemer med komplekse funktioner, der arbejder sammen i et produkt, så du grupperer disse i systemer og tester dem individuelt. (hvis du f.eks. byggede en grafregner, ville du først bygge det aritmetiske 'system' til at håndtere tal, teste, indtil det virker efter hensigten, og derefter ville du bygge og teste det grafiske 'system' for at håndtere tilbagetrækning, som det ville bygge ud af det aritmetiske system). Accepttest er test på brugerniveau; det er at se, om alle systemer kan arbejde synkroniseret for at skabe et færdigt produkt, der er klar til at blive accepteret af brugere (f.eks. brugere, der tester lommeregneren). Softwareudviklere kan nogle gange ignorere dette sidste trin i processen, da virksomheder ofte vil have andre medarbejdere til at implementere brugertest (beta) separat.

Hvordan skriver jeg en JUnit-test i Java?

Nu hvor du har en klarere idé om fordelene og begrænsningerne ved enhedstestning, lad os tage et kig på noget kode! Vi vil bruge en populær Java-testramme kaldet JUnit (en anden populær er TestNG, som du også kan bruge, hvis du vil. De ligner syntaktisk meget hinanden; TestNG er inspireret af JUnit). Du kan downloade og installere JUnit her . For denne eksempelkode vil vi fortsætte med det 'videnskabelige lommeregner' eksempel, som jeg nævnte tidligere; det er ret nemt at vikle hovedet rundt, og testkoden er super nem. Traditionel praksis er at skrive separate testklasser for hver af dine klasser, så det er det, vi gør. Lad os antage, at vi på dette tidspunkt har en Math.javafil med alle matematiske funktioner i den (inklusive Math.add), og vi skriver enMathTests.javafil i samme pakke. Lad os nu opsætte importerklæringer og klassetekst: (MULIGT JUnit-INTERVIEW SPØRGSMÅL: Du bliver muligvis spurgt, hvor du skal placere din JUnit-test, og om du skal importere dine kildefiler eller ej. Hvis du skriver dine testklasser i samme pakke som dine hovedklasser, så behøver du ikke nogen importerklæringer til dine kildefiler i testklassen. Ellers skal du sørge for at importere dine kildefiler!)

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 importerklæring giver os @Testoverskriften. Vi skriver ' @Test' direkte oven på hver testfunktionsdefinition, så JUnit ved, at dette er en enkelt enhedstest, der kan køres separat. Senere vil jeg vise dig, hvordan du kan køre specifikke enhedstest ved hjælp af denne header. Den anden importerklæring sparer os for lidt at skrive. Den primære JUnit-funktion, vi bruger til at teste vores funktioner, er at Assert.assertEquals(), som tager to parametre (faktisk værdi og forventet værdi) og sørger for, at de er ens. At have denne anden importerklæring tillader os bare at skrive ' assertEquals(...' i stedet for at skulle angive hver gang, hvilken pakke den er en del af. Lad os nu skrive en meget simpel testcase for at bekræfte, 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);
	}
}
Lad os gennemgå hver af testfunktionens fem linjer, og hvad de gør: Linje 5: Denne @Testheader specificerer, at funktionsdefinitionen nedenfor add_twoPlusTwo_returnsFour()faktisk er en testfunktion, som JUnit kan køre separat. Linje 6: Dette er funktionssignaturen for vores testcase. Testcases er altid meget enkeltstående; de tester kun ét specifikt eksempel, såsom 2+2=4. Det er konventionelt at navngive dine testcases i formen " [function]_[params]_returns[expected]()," hvor [function]er navnet på den funktion, du tester, [params]er de specifikke eksempelparametre, du tester, og [expected]er den forventede returværdi for funktionen. Testfunktioner har næsten altid en returtype på ' void', fordi hovedpunktet for hele funktionen er at køreassertEquals, som vil sende til konsollen, uanset om din test bestod eller ej; du behøver ikke andre data for at blive returneret nogen steder. Linje 7: Vi erklærer en ' final' variabel af returtypen Math.add (int), og navngiver den 'forventet' efter konvention. Dens værdi er det svar, vi forventer (4). Linje 8: Vi erklærer en ' final' variabel af returtypen af Math.add (int)​​, og kalder den 'faktisk' efter konvention. Dens værdi er resultatet af Math.add(2, 2). Linje 9: Den gyldne streg. Dette er den linje, der sammenligner faktisk og forventet og fortæller os, at vi kun bestod testen, hvis de er ens. Den første parameter, der sendes "2+2 er 4", er en beskrivelse af testfunktionen.

Hvad hvis min funktion skulle give en undtagelse?

Hvis dit specifikke testeksempel skulle kaste en undtagelse i stedet for at hævde, at en faktisk og forventet værdi er ens, så har JUnit en måde at tydeliggøre dette i overskriften @Test. Lad os tage et kig på et eksempel nedenfor. Hvis vi antager, at vi har en funktion i Math.javakaldet Math.divide, vil vi sikre os, at input ikke kan divideres med 0. I stedet bør forsøg på at kalde Math.divide(a, 0)for en "a"-værdi give en undtagelse ( ArithmeticException.class). Vi angiver 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 have mere end én undtagelse for expectedExceptions, bare sørg for at bruge parenteser og kommaer til at angive dine undtagelsesklasser som sådan:

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

Hvordan kører jeg mine JUnit-tests i Java?

Sådan tilføjer du JUnit til IntelliJ: https://stackoverflow.com/questions/19330832/setting-up-junit-with-intellij-idea Du kan køre dit projekt, som du normalt ville køre testene. Ved at køre alle test i en testklasse køres dem i alfabetisk rækkefølge. I JUnit 5 kan du tilføje en prioritet til testene ved at tilføje et @Ordertag. Et eksempel:

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

@Test
@Order (1)
public void b_test() { … }
…
}
Selvom a_test()kommer foran b_test()alfabetisk og i koden, b_test()vil køre før a_test()her, fordi 1 kommer før 2 i rækkefølge. Så det er omtrent alt for det grundlæggende i JUnit. Lad os nu tage fat på et par almindelige JUnit-interviewspørgsmål og lære noget mere om JUnit hen ad vejen!

JUnit-interviewspørgsmål (Yderligere oplysninger)

Her har jeg samlet de mest populære JUnit-interviewspørgsmål. Hvis du har noget at tilføje - er du velkommen til at gøre dette i kommentarerne nedenfor. Q: Hvilken metode kan du kalde i din testmetode for automatisk at mislykkes i en test? A: fail(“fejlbeskrivelse her!”); Q: Du tester en hundeklasse; for at teste et hundeobjekt, skal du instantiere det, før du kan køre test på det. Så du skriver en setUp() funktion for at instansiere hunden. Du ønsker kun at køre denne funktion én gang under hele testen. Hvad skal du sætte direkte over setUp() funktionssignaturen, så JUnit ved at køre setUp() før testen kører? A: @BeforeClass (@BeforeAll i JUnit 5) Q:Hvad skal funktionssignaturen for setUp()-funktionen beskrevet ovenfor? A: offentlig statisk tomrum. Enhver funktion med @BeforeClass (@BeforeAll i JUnit 5) eller @AfterClass (@AfterAll i JUnit 5) skal være statisk. Q: Du er færdig med at teste hundeklassen. Du skriver void tearDown() funktion, som rydder op i data og udskriver information til konsollen efter hver test. Du vil have denne funktion til at køre efter hver eneste test. Hvad skal du sætte direkte over tearDown() funktionssignaturen, så JUnit ved at køre tearDown() efter at have kørt hver test? A: @After (@AfterEach i JUnit 5)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION