JUnit Parameteriserat test med exempel med hjälp av @Parameters
⚡ Smart sammanfattning
Parametriserade tester i JUnit kör samma testmetod upprepade gånger med olika indatavärden, så att en metod täcker många scenarier. Annoteringarna @RunWith och @Parameters tillhandahåller datamängden för varje iteration.
Vad är ett parametriserat test i JUnit?
A parametriserat test är ett test som kör samma testmetod om och om igen med olika värden. Det hjälper utvecklare att spara tid när de skriver tester som bara skiljer sig åt i sina indata och förväntade resultat.
Med hjälp av ett parametriserat test kan man skapa en testmetod som hämtar data från en datakälla. Detta gör det till den enklaste formen av datadrivna tester tillgänglig inuti JUnit sig själv, utan behov av ett externt bibliotek.
Tänk dig ett enkelt test som summerar olika tal. Koden kan se ut så här:
Ovanstående tillvägagångssätt leder till mycket redundans. Varje nytt talpar behöver ytterligare en assert-sats inom samma metod, och ett fel på den första assertionen döljer alla påståenden som följer.
En enklare metod behövs. Med ett parametriserat test kan du lägga till en enda metod som levererar tio datainmatningar, och ditt test kommer att köras tio gånger automatiskt.
Steg för att skapa en parametriserad JUnit Testa
Följande kod visar ett exempel på ett parametriserat test. Det testar sum()-metoden för Airthematic-klassen, vilket är den stavning som används i hela exempelprojektet.
Steg 1) Skapa en klass. I det här exemplet ska vi mata in två tal med hjälp av sum(int, int)-metoden, som returnerar summan av de givna talen.
Steg 2) Skapa en parametriserad testklass.
Code Förklaring
- Code Linje 11: Kommentera din testklass med @RunWith(Parameterized.class).
- Code Linje 13: Deklarerar variabeln 'firstNumber' som privat och typ som int.
- Code Linje 14: Deklarera variabeln 'secondNumber' som privat och av typen som int.
- Code Linje 15: Deklarera variabeln 'expectedResult' som privat och typen som int.
- Code Linje 16: Deklarera variabeln 'airthematic' som privat och typen som Airthematic.
@Spring med(klassnamn.klass): den @Spring med annotering används för att ange dess runnerklassnamn. Om vi inte anger någon typ som parameter kommer körtiden att välja BlockeraJUnit4ClassRunner som standard.
Den här klassen ansvarar för att tester ska köras med en ny testinstans. Den ansvarar för att anropa JUnit livscykelmetoder som installation (associerade resurser) och nedmontering (release-resurser), vilka beskrivs i JUnit test fixtur handledning.
För att parametrisera måste du annotera klassen med @RunWith och skicka den obligatoriska .class som ska testas.
Steg 3) Skapa en konstruktor som lagrar testdata. Den lagrar 3 variabler.
Steg 4) Skapa en statisk metod som genererar och returnerar testdata.
Code Linje 32,33: Skapar en tvådimensionell array (tillhandahåller inparametrar för addition). Med hjälp av asList-metoden konverterar vi data till en List-typ, eftersom returtypen för inmatningsmetoden är en Collection.
Code Linje 30: Använda @Parametrar anteckning för att skapa en uppsättning indata för att köra vårt test.
Den statiska metoden som identifieras av @Parameters-annoteringen returnerar en samling där varje post i samlingen kommer att vara indata för en iteration av testet. Betrakta elementet {1,2,3}. Här:
- förstaNummer = 1
- andraNummer = 2
- förväntat resultat = 3
Här kommer varje element i arrayen att skickas till konstruktorn, ett i taget, allt eftersom klassen instansieras flera gånger. De fem arrayerna som deklareras i exemplet producerar därför följande fem körningar:
| iteration | förstaNummer | andraNummer | förväntat resultat | Konsollinje |
|---|---|---|---|---|
| [0] | 1 | 2 | 3 | Summan av Numbers = : 3 |
| [1] | 11 | 22 | 33 | Summan av Numbers = : 33 |
| [2] | 111 | 222 | 333 | Summan av Numbers = : 333 |
| [3] | 10 | 9 | 19 | Summan av Numbers = : 19 |
| [4] | 100 | 9 | 109 | Summan av Numbers = : 109 |
Steg 5) Den kompletta koden.
Code Förklaring:
- Code Linje 25: Använd @Before-annoteringen för att konfigurera resurserna (här Airthematic.class). @Before-annoteringen används här för att köras före varje testfall. Den innehåller testets förutsättningar.
- Code Linje 36: Använda @Test-annoteringen för att skapa vårt test.
- Code Linje 39: Skapa en hävda uttalande för att kontrollera om vår summa stämmer överens med vad vi förväntade oss.
Steg 6) Skapa en testrunner-klass för att köra det parametriserade testet:
Code Förklaring:
- Code Linje 8: Deklarera huvudmetoden för klassen Test som kommer att köra vår JUnit testet.
- Code Linje 9: Exekvera testfall med hjälp av JUnitCore.runClasses, som tar testklassnamnet som en parameter (i vårt exempel använder vi AirthematicTest.class).
- Code Linje 11: Bearbetar resultatet med en for-loop och skriver ut det misslyckade resultatet.
- Code Linje 13: Skriver ut det lyckade resultatet.
Produktion:
Här är utdata, som visar ett lyckat test utan fel trace, enligt nedan. Observera att JUnit view listar en post per datarad istället för ett enda test:
Se resultatet på konsolen, som visar additionen av de två talen:
Parametriserade tester i JUnit 5 med @ParameterizedTest
Exemplet ovan är skrivet för JUnit 4. JUnit 5 (Jupiter) tar bort löparmodellen helt, så @RunWith(Parameterized.class), datakonstruktorn och instansfälten försvinner. JUnit 4-koden som visas ovan är inte föråldrad: den körs fortfarande oförändrad på JUnit Plattformen genom den gamla motorn. Nya tester skrivs dock normalt med @ParameterizedTest.
Två beroenden krävs: junit-jupiter-api för testannoteringarna och junit-jupiter-parametrar för det parametriserade stödet. Utan den andra artefakten kommer källannoteringarna inte att lösas alls.
import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; class AirthematicTest { // one row per iteration, no constructor and no runner @ParameterizedTest(name = "{0} + {1} = {2}") @CsvSource({"1, 2, 3", "11, 22, 33", "111, 222, 333", "10, 9, 19", "100, 9, 109"}) void sumOfTwoNumbers(int firstNumber, int secondNumber, int expectedResult) { assertEquals(expectedResult, new Airthematic().sum(firstNumber, secondNumber)); } }
Jupiter erbjuder flera argumentkällor, och den rätta beror på dataens form:
| Källanteckning | förnödenheter | Använd den när |
|---|---|---|
| @Värdekälla | En enda kolumn med literaler | Testet tar exakt ett argument |
| @CsvSource | Inline kommaseparerade rader | Små tabeller med siffror och strängar läses tydligt i filen |
| @CsvFileSource | Rader lästa från en CSV-fil på testklassvägen | Datauppsättningen är stor eller underhålls utanför koden |
| @MetodKälla | En statisk fabrik som returnerar en ström av argument | Verkliga objekt, beräknade värden eller randomiserade data behövs |
| @EnumSource | Konstanterna i en enum | Varje enumvärde måste utnyttjas |
Två regler drabbar de flesta nybörjare. En källkodsannotering som placeras på en vanlig @Test-metod ignoreras tyst, så metoden måste bära @ParameterizedTest. Och ett tomt värde i @CsvSource utan citat läses som null, medan ett tomt värde i citat läses som en tom sträng.
Ocuco-landskapet JUnit 4 annoteringar som används i den här artikeln mappas till Jupiter enligt följande: @RunWith(Parameterized.class) blir @ParameterizedTest plus en källannotering, @Parameters blir @MethodSource eller @CsvSource, och @Before blir @BeforeEach. Hela listan finns i JUnit annoteringar handledning.
Fördelar och begränsningar med parametriserade tester
Parameterisering är inte gratis. Det eliminerar dubbelarbete, men det begränsar också hur ett test kan skrivas, så det är värt att känna till båda sidor innan man konverterar en befintlig svit.
Fördelar
- Less duplicering: En metod ersätter ett block med nästan identiska assert-satser, som den första skärmdumpen i den här artikeln visar.
- Billigare försäkring: Att lägga till ett edge-case kostar en extra datarad istället för en helt ny testfall metod.
- Noggrann rapportering: Varje iteration rapporteras separat, så JUnit vyn identifierar exakt vilken rad som misslyckades snarare än ett aggregerat fel.
- Centraliserade data: Indata finns i en enda metod och kan senare flyttas till en CSV-fil eller en fabrik utan att påverka assertionerna.
Begränsningar
- En form av påstående: Varje rad kör samma påståenden, så ett scenario som behöver olika kontroller behöver fortfarande sin egen testmetod.
- Klassnivåomfattning i JUnit 4: Löparen parametriserar hela klassen, så orelaterade @Test-metoder i den klassen körs också en gång per rad.
- Oläsliga rapporter: Utan en namnmall visas fel som testAirthematicTest[3], vilket inte säger något om vilken data som gick sönder.
- Stora inline-data: Stora arrayer tränger ut testlogiken; flytta dem till @CsvFileSource eller en @MethodSource-fabrik istället.
Vanliga fel i JUnit Parametriserade tester
De flesta parametriserade fel är initialiseringsfel som uppstår innan en enskild assertion körs. Tabellen nedan listar de meddelanden som visas oftast och vad som utlöser dem.
| Meddelande | Orsak | Fast |
|---|---|---|
| Testklassen ska ha exakt en offentlig konstruktor | Klassen deklarerar ingen, eller två, offentliga konstruktorer | Behåll en offentlig konstruktor vars parametrar matchar datakolumnerna |
| Ingen offentlig statisk parametermetod på klassen | Metoden @Parameters är inte offentlig statisk, eller returnerar fel typ | Deklarera den som en offentlig statisk samling och returnera Arrays.asList(…) |
| IllegalArgumentException: fel antal argument | En rad är bredare eller smalare än konstruktorns parameterlista | Gör varje array i samlingen lika bred som konstruktorn |
| Konfigurationsfel: inga argumentleverantörer | Ett Jupiter-test bär @ParameterizedTest utan källkodsannotering | Lägg till @ValueSource, @CsvSource, @CsvFileSource, @MethodSource eller @EnumSource |
| Källanteckningen verkar inte göra någonting | Metoden annoteras @Test istället för @ParameterizedTest | Ersätt @Test med @ParameterizedTest och importera junit-jupiter-params |
Ytterligare en fälla är delat tillstånd. Eftersom JUnit bygger en ny instans per rad, allt som finns i ett statiskt fält överlever varje iteration, och ett värde som skrivs av rad [0] kan i tysthet ändra resultatet av rad [4]. Behåll tillstånd per rad i instansfält och återställ delade resurser i @Before- eller @BeforeEach-metoden. Allmän vägledning om att isolera tester finns i enhetstestning handledning.









