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.

  • 🔘 Datakälla: En statisk @Parameters-metod returnerar en samling arrayer, och varje array blir en testiteration.
  • ☑️ Löpare: @RunWith(Parameterized.class) ersätter standardblocketJUnit4ClassRunner och återskapar klassen en gång per datarad.
  • Byggare: En offentlig konstruktor lagrar en enda rad med data i de instansfält som testmetoden gör assertioner mot.
  • 🧪 Utarbetat exempel: Fem inmatningsrader driver sum()-testet, och JUnit visa rapporter Körs 5/5 med noll fel.
  • 🛠️ JUnit 5: @ParameterizedTest med @ValueSource, @CsvSource eller @MethodSource tar bort både löparen och konstruktorn.
  • 📌 Fallgropar: En icke-statisk @Parameters-metod, två publika konstruktorer eller ett saknat junit-jupiter-params-beroende stoppar körningen.

JUnit parametriserat test med @RunWith- och @Parameters-annoteringar

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:

JUnit testmetod som upprepar tre assertEquals-anrop för summetoden

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.

Airthematic-klassen deklarerar en public sum-metod som lägger till två int-argument

Steg 2) Skapa en parametriserad testklass.

Testklasshuvud kommenterat med @RunWith(Parameterized.class) och fyra privata fält

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.

Parameteriserad testkonstruktor som tilldelar tre heltalsargument till instansfält

Steg 4) Skapa en statisk metod som genererar och returnerar testdata.

Statisk inmatningsmetod kommenterad med @Parameterized.Parameters som returnerar en tvådimensionell objektmatris

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.

Komplett AirthematicTest-lista med importer, konstruktor, @Parameters-metoden och @Test-metoden

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:

TestRunner-klassen skickar AirthematicTest.class till JUnitCore.runClasses och utskriftsfel

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:

Eclipse JUnit visa rapportering Körningar 5/5 med 0 fel och 0 misslyckanden för den parametriserade klassen

Se resultatet på konsolen, som visar additionen av de två talen:

Eclipse konsolutskrift en Summa av Numbers rad för var och en av de fem parameterraderna

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.

Vanliga frågor

TestNG levererar rader via en @DataProvider-metod som refereras per testmetod, så orelaterade tester i klassen påverkas inte. JUnit 4 parametriserar hela klassen genom sin runner. JUnit 5 stänger det gapet med per-metoden @ParameterizedTest.

Ja. JUnit 4 accepterar @Parameters(name = “{index}: sum({0},{1})={2}”) och Jupiter accepterar @ParameterizedTest(name = “…”). Platshållare ersätts vid körning, så en felrapport namnger den felande raden istället för att visa ett rent index.

Ja. JUnit 4 stöder @Parameter(0) och @Parameter(1) på publika icke-statiska fält, och klassen förlitar sig sedan på standardkonstruktorn. Att kombinera fältinjektion med en datakonstruktor utlöser felet "exactly-one-public-constructor".

JUnit 4 behöver bara junit-artefakten, eftersom den parametriserade löparen skickas inuti den. JUnit 5 behöver junit-jupiter-params tillsammans med junit-jupiter-api; utan den artefakten misslyckas @ParameterizedTest och alla källkodsannoteringar att lösas.

Jupiter tillhandahåller @CsvFileSource(resources = “/data.csv”, numLinesToSkip = 1), som läser rader från testklasssökvägen. JUnit 4 har ingen inbyggd motsvarighet, så @Parameters-metoden måste öppna och tolka själva filen innan samlingen returneras.

In JUnit 4 kan det, men runnern parametriserar hela klassen, så varje metod körs en gång per datarad. Jupiter parametriserar individuella metoder, så vanliga @Test-metoder i samma klass körs fortfarande exakt en gång.

AI-assistenter läser en metodsignatur och föreslår gränsrader som noll-, negativ-, maximal- och överflödesingångar som en handskriven tabell ofta missar. RevVisa varje genererat förväntat resultat, eftersom en modell kan producera en rimlig rad med fel svar.

GitHub Copilot producerar ställningen snabbt men blandas ofta JUnit 4 och Jupiter importerar, och lämnar ibland en källkodsannotering på en vanlig @Test-metod. Kontrollera importerna innan du kör sviten.

Sammanfatta detta inlägg med: