JUnit Geparametriseerde test met voorbeeld met @Parameters
⚡ Slimme samenvatting
Geparameteriseerde tests in JUnit Voer dezelfde testmethode herhaaldelijk uit met verschillende invoerwaarden, zodat één methode veel scenario's dekt. De annotaties @RunWith en @Parameters leveren de dataset voor elke iteratie.
Wat is een geparameteriseerde test in JUnit?
A geparameteriseerde test Een test die dezelfde testmethode steeds opnieuw uitvoert met verschillende waarden, helpt ontwikkelaars tijd te besparen bij het schrijven van tests die alleen verschillen in hun invoer en verwachte resultaten.
Met behulp van een geparameteriseerde test kan men een testmethode opzetten die gegevens ophaalt uit een bepaalde gegevensbron. Dit maakt het de eenvoudigste vorm van... datagedreven testen beschikbaar binnen JUnit werkt vanzelf, zonder dat er een externe bibliotheek nodig is.
Neem bijvoorbeeld een eenvoudige test die verschillende getallen optelt. De code zou er als volgt uit kunnen zien:
De bovenstaande aanpak leidt tot veel redundantie. Elk nieuw paar getallen vereist een nieuwe assert-instructie binnen dezelfde methode, en een fout bij de eerste assertie maskeert alle daaropvolgende asserties.
Een eenvoudigere aanpak is nodig. Met behulp van een geparameteriseerde test kunt u een enkele methode toevoegen die tien gegevensinvoerwaarden levert, waarna uw test automatisch tien keer wordt uitgevoerd.
Stappen voor het maken van een geparametriseerd JUnit Test
De volgende code toont een voorbeeld van een geparameteriseerde test. Deze test de `sum()`-methode van de `Airthematic`-klasse, de spelling die in het hele voorbeeldproject wordt gebruikt.
Stap 1) Maak een klasse aan. In dit voorbeeld gaan we twee getallen invoeren met behulp van de methode `sum(int, int)`, die de som van de gegeven getallen retourneert.
Stap 2) Maak een geparameteriseerde testklasse aan.
Code Uitleg
- Code Regel 11: Voorzie je testklasse van de annotatie @RunWith(Parameterized.class).
- Code Regel 13: De variabele 'firstNumber' declareren als private en type als int.
- Code Regel 14: De variabele 'secondNumber' declareren als privé en type als int.
- Code Regel 15: De variabele 'expectedResult' declareren als privé en type als int.
- Code Regel 16: De variabele 'airthematic' declareren als privé en type als Airthematic.
@Rennen met(class_name.class): de @Rennen met De annotatie wordt gebruikt om de naam van de runner-klasse te specificeren. Als we geen type als parameter specificeren, kiest de runtime een ander type. BlockJUnit4ClassRunner standaard.
Deze klasse is verantwoordelijk voor het uitvoeren van tests met een nieuwe testinstantie. Het is verantwoordelijk voor het aanroepen van... JUnit Levenscyclusmethoden zoals setup (het koppelen van resources) en teardown (het vrijgeven van resources), die worden beschreven in de JUnit test armatuur tutorial.
Om parameters toe te voegen, moet je de klasse annoteren met @RunWith en de vereiste .class doorgeven die getest moet worden.
Stap 3) Maak een constructor die de testgegevens opslaat. Deze slaat 3 variabelen op.
Stap 4) Creëer een statische methode die testgegevens genereert en retourneert.
Code Regel 32,33: Een tweedimensionale array maken (met invoerparameters voor optellen). Met de `asList`-methode converteren we de gegevens naar een lijsttype, aangezien het retourtype van de `input`-methode een `Collection` is.
Code Regel 30: De @Parameters annotatie om een set invoergegevens te maken om onze test uit te voeren.
De statische methode die wordt aangeduid met de annotatie @Parameters retourneert een Collection, waarbij elk element in de Collection de invoergegevens voor één iteratie van de test vormt. Neem bijvoorbeeld het element {1,2,3}. Hier:
- eersteNummer = 1
- tweedeNummer = 2
- verwacht resultaat = 3
Hier wordt elk element van de array één voor één aan de constructor doorgegeven, aangezien de klasse meerdere keren wordt geïnstantieerd. De vijf arrays die in het voorbeeld zijn gedeclareerd, leveren daarom de volgende vijf uitvoeringen op:
| herhaling | eersteNummer | tweede nummer | verwacht resultaat | Console-regel |
|---|---|---|---|---|
| [0] | 1 | 2 | 3 | Som van Numbers = : 3 |
| [1] | 11 | 22 | 33 | Som van Numbers = : 33 |
| [2] | 111 | 222 | 333 | Som van Numbers = : 333 |
| [3] | 10 | 9 | 19 | Som van Numbers = : 19 |
| [4] | 100 | 9 | 109 | Som van Numbers = : 109 |
Stap 5) De volledige code.
Code Uitleg:
- Code Regel 25: De `@Before`-annotatie wordt gebruikt om de resources (in dit geval `Airthematic.class`) in te stellen. De `@Before`-annotatie wordt hier gebruikt om vóór elke testcase te worden uitgevoerd. Deze bevat de voorwaarde voor de test.
- Code Regel 36: We gebruiken de @Test-annotatie om onze test te maken.
- Code Regel 39: Een maken bewering verklaring om te controleren of onze som overeenkomt met wat we verwachtten.
Stap 6) Maak een testrunnerklasse aan om de geparameteriseerde test uit te voeren:
Code Uitleg:
- Code Regel 8: De hoofdmethode van de klasse Test declareren, die onze test zal uitvoeren. JUnit test.
- Code Regel 9: Testcases uitvoeren met behulp van JUnitCore.runClasses, die de naam van de testklasse als parameter accepteert (in ons voorbeeld gebruiken we AirthematicTest.class).
- Code Regel 11: Het resultaat verwerken met een for-loop en het mislukte resultaat afdrukken.
- Code Regel 13: Het succesvolle resultaat afdrukken.
Output:
Hier is de uitvoer, die een succesvolle test zonder fouten laat zien. trace, zoals hieronder weergegeven. Merk op dat de JUnit De weergave toont één item per gegevensrij in plaats van één enkele test:
Bekijk het resultaat in de console, dat de optelling van de twee getallen laat zien:
Geparameteriseerde tests in JUnit 5 met @ParameterizedTest
Het bovenstaande voorbeeld is geschreven voor JUnit 4. JUnit 5 (Jupiter) laat het runner-model volledig vallen, waardoor @RunWith(Parameterized.class), de data-constructor en de instantievelden allemaal verdwijnen. JUnit De bovenstaande code (4) is niet verouderd: deze werkt nog steeds ongewijzigd. JUnit Het platform maakt gebruik van de vintage engine. Nieuwe tests worden echter doorgaans geschreven met @ParameterizedTest.
Twee afhankelijkheden zijn vereist: junit-jupiter-api voor de testannotaties en junit-jupiter-params voor de geparameteriseerde ondersteuning. Zonder het tweede artefact zullen de bronannotaties helemaal niet worden opgelost.
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 biedt verschillende argumentatiebronnen aan, en de juiste hangt af van de vorm van de gegevens:
| Bronannotatie | Onderdelen | Gebruik het wanneer |
|---|---|---|
| @Waardebron | Een enkele kolom met letterlijke waarden | De test vereist precies één argument. |
| @CsvSource | Inline door komma's gescheiden rijen | Kleine tabellen met getallen en tekenreeksen worden probleemloos in het bestand gelezen. |
| @CsvFileSource | Rijen gelezen uit een CSV-bestand op het testclasspath. | De dataset is groot of wordt buiten de code beheerd. |
| @MethodSource | Een statische fabriek die een stroom argumenten retourneert. | Er zijn echte objecten, berekende waarden of willekeurige gegevens nodig. |
| @EnumSource | De constanten van een enum | Elke enum-waarde moet worden getest. |
Twee regels zijn voor de meeste beginners een struikelblok. Een source-annotatie op een gewone @Test-methode wordt stilzwijgend genegeerd, dus de methode moet @ParameterizedTest bevatten. En een lege waarde zonder aanhalingstekens in @CsvSource wordt gelezen als null, terwijl een lege waarde met aanhalingstekens wordt gelezen als een lege tekenreeks.
De JUnit De 4 annotaties die in dit artikel worden gebruikt, worden als volgt op Jupiter weergegeven: @RunWith(Parameterized.class) wordt @ParameterizedTest plus een bronannotatie, @Parameters wordt @MethodSource of @CsvSource, en @Before wordt @BeforeEach. De volledige lijst wordt behandeld in het artikel. JUnit annotaties tutorial.
Voordelen en beperkingen van geparameteriseerde tests
Parameterisering is niet zonder kosten. Het voorkomt duplicatie, maar het beperkt ook de manier waarop een test geschreven kan worden. Daarom is het de moeite waard om beide kanten te kennen voordat je een bestaande testsuite converteert.
Voordelen
- Less duplicatie: Eén methode vervangt een blok met vrijwel identieke assert-instructies, zoals de eerste schermafbeelding in dit artikel laat zien.
- Goedkopere verzekering: Het toevoegen van een randgeval kost één extra gegevensrij in plaats van een geheel nieuwe. testcase methode.
- Nauwkeurige rapportage: Elke iteratie wordt afzonderlijk gerapporteerd, dus de JUnit De weergave geeft precies aan welke rij is mislukt, in plaats van één geaggregeerde fout.
- Gecentraliseerde gegevens: De invoergegevens bevinden zich in één methode en kunnen later naar een CSV-bestand of een factory worden verplaatst zonder de assertions aan te passen.
Beperkingen
- Een vorm van bewering: Elke rij voert dezelfde controles uit, dus een scenario dat andere controles vereist, heeft nog steeds een eigen testmethode nodig.
- Bereik op klasseniveau in JUnit 4: De runner parameteriseert de hele klasse, waardoor ook niet-gerelateerde @Test-methoden in die klasse eenmaal per regel worden uitgevoerd.
- Onleesbare rapporten: Zonder een naamsjabloon verschijnen fouten als testAirthematicTest[3], wat niets zegt over de gegevens die kapot zijn gegaan.
- Omvangrijke inline-gegevens: Grote arrays nemen veel ruimte in beslag voor de testlogica; verplaats ze daarom naar een @CsvFileSource of een @MethodSource factory.
Veelvoorkomende fouten in JUnit Geparameteriseerde tests
De meeste geparameteriseerde fouten zijn initialisatiefouten die optreden voordat een enkele bewering wordt uitgevoerd. De onderstaande tabel geeft een overzicht van de meest voorkomende berichten en wat deze veroorzaakt.
| Typ hier uw vraag of bericht | Veroorzaken | Bepalen |
|---|---|---|
| De testklasse moet precies één openbare constructor hebben. | De klasse declareert geen openbare constructor, of twee ervan. | Houd één openbare constructor aan waarvan de parameters overeenkomen met de gegevenskolommen. |
| Geen openbare statische parametermethode op klasse | De @Parameters-methode is niet openbaar statisch, of retourneert een onjuist type. | Declareer het als public static Collection en retourneer Arrays.asList(…) |
| IllegalArgumentException: onjuist aantal argumenten | Een rij is breder of smaller dan de parameterlijst van de constructor. | Zorg ervoor dat elke array in de collectie dezelfde breedte heeft als de constructor. |
| Configuratiefout: geen argumentenprovider | Een Jupiter-test bevat @ParameterizedTest zonder bronannotatie. | Voeg @ValueSource, @CsvSource, @CsvFileSource, @MethodSource of @EnumSource toe. |
| De bronvermelding lijkt geen effect te hebben. | De methode is voorzien van de annotatie @Test in plaats van @ParameterizedTest. | Vervang @Test door @ParameterizedTest en importeer junit-jupiter-params |
Een andere valkuil is gedeelde status. Omdat JUnit bouwt een nieuwe instantie per rij, alles wat in een statisch veld wordt bewaard, blijft behouden bij elke iteratie, en een waarde die door rij [0] wordt geschreven, kan stilletjes de uitkomst van rij [4] wijzigen. Bewaar de status per rij in instantievelden en reset gedeelde resources in de @Before- of @BeforeEach-methode. Algemene richtlijnen voor het isoleren van tests worden behandeld in de testen van een eenheid tutorial.









