CodeGym /Java blogg /Slumpmässig /Enhetstestning i Java med JUnit
John Squirrels
Nivå
San Francisco

Enhetstestning i Java med JUnit

Publicerad i gruppen

Vad är enhetstestning i Java?

Innan vi börjar lära oss JUnit i Java, låt oss kortfattat överblicka vad enhetstestning är och varför det är så populärt (om du redan kan det här, hoppa till 'Hur skriver jag ett JUnit-test i Java?'). Enhetstestning i Java gör storskalig mjukvaruutveckling mycket mer effektiv och enkel. Det kan hjälpa både individer och team att minska otaliga timmar på felsökning och effektivisera samarbetsprocessen oerhört. Enhetstestning i Java med JUnit - 1

https://junit.org/junit4/

Den väsentliga idén med enhetstestning är denna: skriv atomtester av individuella egenskaper (kallade enhetstester) och lägg långsamt till fler funktioner efter att ha testat och se till att de tidigare fungerar. Det är en extremt enkel men kraftfull idé. Som ett exempel på hur denna process kan se ut, föreställ dig att du byggde en virtuell vetenskaplig kalkylator. Utöver de skenbara aritmetiska operatorerna ( , +, -, x) %, skulle denna kalkylator ha avancerade funktioner som kräver andra underfunktioner för att fungera inom den. För att beräkna exponenter måste din miniräknare kunna multiplicera korrekt. Så en enhetstestningsmetod för att bygga och testa denna kalkylator skulle vara:
  • Skriv en additionsfunktion. Testa det noggrant, byt det, upprepa tills det fungerar.
  • Gör samma sak för funktionerna subtraktion, multiplikation, division.
  • Använd dessa basoperatorer för att skriva mer avancerade operatorfunktioner som exponenter och testa sedan dessa funktioner också.
Detta säkerställer att funktioner som bygger på andra mindre underfunktioner inte bara fungerar korrekt i sig utan att de inte har felaktiga underfunktioner. Till exempel, om jag testar exponentfunktionen och något går fel, vet jag att felet förmodligen inte finns i multiplikationsunderfunktionen, eftersom multiplikationsfunktionen redan testades utförligt. Detta eliminerar avsevärt den totala mängden kod jag behöver spåra och inspektera för att hitta felet. Förhoppningsvis klargör detta triviala exempel hur tankeprocessen kring Unit Testing är uppbyggd. Men hur samverkar enhetstestning med resten av mjukvaruutvecklingsprocessen? Tänk om du har ännu mer komplexa funktioner, som behöver kunna fungera och kommunicera tillsammans? Enhetstestning är otillräcklig för att säkerställa att sådana komplexa funktioner kan fungera korrekt tillsammans. I själva verket är det bara det första steget i de fyra nivåerna av mjukvarutestning (jag använder stora bokstäver eftersom jag hänvisar till industristandarden eller den vanligaste metoden för att testa programvara). De tre sista stegen ärIntegrationstestning , systemtestning och acceptanstestning. Alla dessa betyder förmodligen exakt vad du tror att de gör, men låt mig förtydliga: Integrationstestning är vad vi skulle göra för att säkerställa att de som nämnts ovan, "komplexa funktioner", interagerar korrekt med varandra. (t.ex. se till att räknaren kan hantera "3 + 7 * 4 - 2") Systemtestning är att testa den övergripande designen av ett visst system; Det finns ofta flera system med komplexa funktioner som samverkar i en produkt, så du grupperar dessa i system och testar dem individuellt. (om du t.ex. byggde en grafräknare, skulle du först bygga det aritmetiska "systemet" för att hantera siffror, testa tills det fungerar som det är tänkt, och sedan skulle du bygga och testa det grafiska "systemet" för att hantera uttag, som det skulle bygga på det aritmetiska systemet). Acceptanstestning är testning på användarnivå; det är att se om alla system kan arbeta synkroniserat för att skapa en färdig produkt redo att accepteras av användare (t.ex. användare som testar räknaren). Mjukvaruutvecklare kan ibland ignorera detta sista steg i processen, eftersom företag ofta kommer att låta andra anställda implementera användar- (beta) tester separat.

Hur skriver jag ett JUnit-test i Java?

Nu när du har en tydligare uppfattning om fördelarna och begränsningarna med enhetstestning, låt oss ta en titt på lite kod! Vi kommer att använda ett populärt Java-testramverk som heter JUnit (en annan populär är TestNG, som du också kan använda om du vill. De är syntaktiskt väldigt lika; TestNG är inspirerad av JUnit). Du kan ladda ner och installera JUnit här . För den här exempelkoden fortsätter vi från exemplet "vetenskaplig kalkylator" som jag nämnde tidigare; det är ganska enkelt att linda huvudet, och testkoden är superenkel. Vanlig praxis är att skriva separata testklasser för var och en av dina klasser, så det är vad vi kommer att göra. Låt oss anta att vi vid det här laget har en Math.javafil med alla matematiska funktioner i den (inklusive ), Math.addoch vi skriver enMathTests.javafil i samma paket. Låt oss nu ställa in importsatser och klasskropp: (MÖJLIG JUnit INTERVJUFRÅGA: Du kan bli tillfrågad var du ska placera ditt JUnit-test och om du behöver importera dina källfiler eller inte. Om du skriver dina testklasser i samma paket som dina huvudklasser, då behöver du inga importsatser för dina källfiler i testklassen. Se annars till att du importerar dina källfiler!)

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örsta importsatsen ger oss @Testrubriken. Vi skriver ' @Test' direkt ovanpå varje testfunktionsdefinition, så att JUnit vet att detta är ett singulär enhetstest som kan köras separat. Senare kommer jag att visa dig hur du kan köra specifika enhetstester med den här rubriken. Den andra importsatsen sparar oss lite att skriva. Den primära JUnit-funktionen vi använder för att testa våra funktioner är att , Assert.assertEquals()som tar två parametrar (verkligt värde och förväntat värde) och ser till att de är lika. Genom att ha denna andra importsats kan vi bara skriva ' assertEquals(...' istället för att varje gång behöva specificera vilket paket det är en del av. Låt oss nu skriva ett mycket enkelt testfall för att verifiera att 2 + 2 verkligen är 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);
	}
}
Låt oss gå igenom var och en av testfunktionens fem rader och vad de gör: Rad 5: Denna @Testrubrik anger att funktionsdefinitionen nedan add_twoPlusTwo_returnsFour()verkligen är en testfunktion som JUnit kan köra separat. Rad 6: Detta är funktionssignaturen för vårt testfall. Testfall är alltid mycket singulara; de testar bara ett specifikt exempel, som 2+2=4. Det är vanligt att namnge dina testfall i formen " , [function]_[params]_returns[expected]()" där [function]är namnet på funktionen du testar, [params]är de specifika exempelparametrarna du testar och [expected]är det förväntade returvärdet för funktionen. Testfunktioner har nästan alltid returtypen ' void' eftersom huvudpoängen med hela funktionen är att körasassertEquals, som kommer att mata ut till konsolen om ditt test godkänts eller inte; du behöver ingen annan data för att returneras någonstans. Rad 7: Vi deklarerar en ' final' variabel av returtypen Math.add (int), och namnger den 'förväntad' enligt konventionen. Dess värde är det svar vi förväntar oss (4). Rad 8: Vi deklarerar en ' final' variabel av returtypen av Math.add (int), och namnger den 'faktisk' enligt konvention. Dess värde är resultatet av Math.add(2, 2). Rad 9: Den gyllene linjen. Detta är raden som jämför faktiska och förväntade och talar om för oss att vi klarade testet endast om de är lika. Den första parametern som skickas "2+2 är 4" är en beskrivning av testfunktionen.

Vad händer om min funktion skulle skapa ett undantag?

Om ditt specifika testexempel skulle ge ett undantag istället för att hävda att ett verkligt och förväntat värde är lika, så har JUnit ett sätt att förtydliga detta i rubriken @Test. Låt oss ta en titt på ett exempel nedan. Om vi ​​antar att vi har en funktion i Math.javacall Math.dividevill vi se till att indata inte kan delas med 0. Om du istället försöker kalla Math.divide(a, 0)på ett "a"-värde bör ett undantag skapas ( ArithmeticException.class). Vi anger det i rubriken 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 än ett undantag för expectedExceptions, se bara till att använda parenteser och kommatecken för att lista dina undantagsklasser, som sådana:

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

Hur kör jag mina JUnit-tester i Java?

Så här lägger du till JUnit till IntelliJ: https://stackoverflow.com/questions/19330832/setting-up-junit-with-intellij-idea Du kan köra ditt projekt som du normalt skulle köra testerna. Att köra alla tester i en testklass kommer att köra dem i alfabetisk ordning. I JUnit 5 kan du lägga till en prioritet till testerna genom att lägga till en @Ordertagg. Ett exempel:

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

@Test
@Order (1)
public void b_test() { … }
…
}
Även om a_test()kommer före b_test()alfabetiskt och i koden, b_test()kommer att köras före a_test()här, eftersom 1 kommer före 2 i ordning. Så det är ungefär allt för grunderna i JUnit. Låt oss nu ta itu med ett par vanliga JUnit-intervjufrågor och lära oss lite mer om JUnit på vägen!

JUnit intervjufrågor (ytterligare information)

Här har jag samlat de mest populära JUnit-intervjufrågorna. Om du har något att tillägga - gör gärna detta i kommentarerna nedan. F: Vilken metod kan du anropa i din testmetod för att automatiskt misslyckas i ett test? A: fail(“felbeskrivning här!”); F: Du testar en hundklass; för att testa ett hundobjekt måste du instansiera det innan du kan köra tester på det. Så du skriver en setUp()-funktion för att instansiera hunden. Du vill bara köra den här funktionen en gång under hela testningen. Vad måste du sätta direkt ovanför setUp() funktionssignaturen så att JUnit vet att köra setUp() innan testerna körs? S: @BeforeClass (@BeforeAll i JUnit 5) F:Vad måste vara funktionssignaturen för setUp()-funktionen som beskrivs ovan? S: offentligt statiskt tomrum. Alla funktioner med @BeforeClass (@BeforeAll i JUnit 5) eller @AfterClass (@AfterAll i JUnit 5) måste vara statiska. F: Du är klar med att testa hundklassen. Du skriver void tearDown() funktion som rensar upp data och skriver ut information till konsolen efter varje test. Du vill att den här funktionen ska köras efter varje enskilt test. Vad måste du sätta direkt ovanför tearDown() funktionssignaturen så att JUnit vet att köra tearDown() efter att ha kört varje test? S: @After (@AfterEach i JUnit 5)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION