JUnit Test sparametryzowany z przykładem przy użyciu @Parameters

⚡ Inteligentne podsumowanie

Testy sparametryzowane w JUnit Uruchamiaj tę samą metodę testową wielokrotnie z różnymi wartościami wejściowymi, aby jedna metoda obejmowała wiele scenariuszy. Adnotacje @RunWith i @Parameters dostarczają zestaw danych dla każdej iteracji.

  • 🔘 Źródło danych: Statyczna metoda @Parameters zwraca kolekcję tablic, a każda tablica staje się jedną iteracją testu.
  • Biegacz: @RunWith(Parameterized.class) zastępuje domyślny blokJUnit4ClassRunner i przebudowuje klasę raz dla każdego wiersza danych.
  • Budowniczy: Jeden konstruktor publiczny przechowuje pojedynczy wiersz danych w polach instancji, względem których metoda testowa przeprowadza asercję.
  • 🧪 Przykład rozwiązania: Pięć wierszy wejściowych steruje testem sum(), a JUnit zobacz raporty Działa 5/5 bez żadnych awarii.
  • 🛠️. JUnit 5: @ParameterizedTest z @ValueSource, @CsvSource lub @MethodSource usuwa zarówno program uruchamiający, jak i konstruktor.
  • 📌 Pułapki: Metoda @Parameters niestatyczna, dwa konstruktory publiczne lub brakująca zależność junit-jupiter-params powodują zatrzymanie wykonania.

JUnit sparametryzowany test z użyciem adnotacji @RunWith i @Parameters

Czym jest test parametryczny w JUnit?

A test sparametryzowany Test, który wielokrotnie uruchamia tę samą metodę testową, używając różnych wartości. Pomaga programistom zaoszczędzić czas podczas pisania testów, które różnią się jedynie danymi wejściowymi i oczekiwanymi wynikami.

Używając testu sparametryzowanego, można skonfigurować metodę testową, która pobiera dane z pewnego źródła danych. To najprostsza forma testowanie oparte na danych dostępne w środku JUnit samego siebie, bez potrzeby korzystania z żadnej zewnętrznej biblioteki.

Rozważmy prosty test sumujący różne liczby. Kod może wyglądać tak:

JUnit metoda testowa powtarzająca trzy wywołania assertEquals dla metody sum

Powyższe podejście prowadzi do dużej redundancji. Każda nowa para liczb wymaga kolejnego polecenia assert w tej samej metodzie, a błąd pierwszego polecenia ukrywa każde kolejne polecenie.

Potrzebne jest prostsze podejście. Używając sparametryzowanego testu, możesz dodać jedną metodę, która dostarcza dziesięć danych wejściowych, a test zostanie automatycznie uruchomiony dziesięć razy.

Kroki tworzenia pliku sparametryzowanego JUnit Testowanie

Poniższy kod przedstawia przykład testu sparametryzowanego. Testuje on metodę sum() klasy Airthematic, której pisownia jest użyta w całym przykładowym projekcie.

Krok 1) Utwórz klasę. W tym przykładzie wprowadzimy dwie liczby za pomocą metody sum(int, int), która zwróci sumę podanych liczb.

Klasa Airthematic deklarująca publiczną metodę sumy, która dodaje dwa argumenty int

Krok 2) Utwórz sparametryzowaną klasę testową.

Nagłówek klasy testowej z adnotacją @RunWith(Parameterized.class) i czterema prywatnymi polami

Code Wyjaśnienie

  • Code Linia 11: Dodaj adnotację do swojej klasy testowej przy użyciu @RunWith(Parameterized.class).
  • Code Linia 13: Deklarowanie zmiennej „firstNumber” jako prywatnej i wpisywanie jako int.
  • Code Linia 14: Deklarowanie zmiennej „secondNumber” jako prywatnej i typu int.
  • Code Linia 15: Deklarowanie zmiennej „expectedResult” jako prywatnej i typu int.
  • Code Linia 16: Deklaracja zmiennej „airthematic” jako prywatnej i typu Airthematic.

@Biegnij z(nazwa_klasy.klasa): @Biegnij z Adnotacja służy do określenia nazwy klasy wykonawczej. Jeśli nie określimy żadnego typu jako parametru, środowisko wykonawcze wybierze BlokowaćJUnit4ClassRunner domyślnie.

Ta klasa odpowiada za uruchamianie testów z nową instancją testu. Odpowiada za wywoływanie JUnit metody cyklu życia, takie jak konfiguracja (zasoby skojarzone) i demontaż (zasoby zwalniające), które są opisane w JUnit urządzenie testowe poradnik.

Aby sparametryzować, należy dodać adnotację @RunWith do klasy i przekazać wymagany plik .class, który ma zostać przetestowany.

Krok 3) Utwórz konstruktor, który przechowuje dane testowe. Przechowuje on 3 zmienne.

Parametryzowany konstruktor testu przypisujący trzy argumenty int do pól instancji

Krok 4) Utwórz metodę statyczną, która generuje i zwraca dane testowe.

Statyczna metoda wejściowa z adnotacją @Parameterized.Parameters zwracająca dwuwymiarową tablicę obiektów

Code Linia 32,33: Tworzymy tablicę dwuwymiarową (z parametrami wejściowymi do dodawania). Za pomocą metody asList konwertujemy dane na typ List, ponieważ typem zwracanym przez metodę input jest Collection.

Code Linia 30: Korzystanie z @Parametry adnotacja tworząca zestaw danych wejściowych do uruchomienia naszego testu.

Metoda statyczna zidentyfikowana przez adnotację @Parameters zwraca kolekcję, w której każdy wpis będzie danymi wejściowymi dla jednej iteracji testu. Rozważmy element {1,2,3}. Tutaj:

  • firstNumber = 1
  • drugaLiczba = 2
  • oczekiwanyWynik = 3

Tutaj każdy element tablicy będzie przekazywany do konstruktora pojedynczo, ponieważ klasa jest tworzona wielokrotnie. Pięć tablic zadeklarowanych w przykładzie generuje zatem następujące pięć przebiegów:

Iteracja pierwszyNumer drugi numer oczekiwanyWynik Linia konsol
[0] 1 2 3 Suma Numbers = : 3
[1] 11 22 33 Suma Numbers = : 33
[2] 111 222 333 Suma Numbers = : 333
[3] 10 9 19 Suma Numbers = : 19
[4] 100 9 109 Suma Numbers = : 109

Krok 5) Kompletny kod.

Pełna lista AirthematicTest z importami, konstruktorem, metodą @Parameters i metodą @Test

Code Wyjaśnienie:

  • Code Linia 25: Użycie adnotacji @Before do skonfigurowania zasobów (tutaj Airthematic.class). Adnotacja @Before jest tutaj używana do uruchomienia przed każdym przypadkiem testowym. Zawiera ona warunek wstępny testu.
  • Code Linia 36: Utworzenie testu przy użyciu adnotacji @Test.
  • Code Linia 39: Tworzenie oświadczenie assert aby sprawdzić czy nasza suma jest równa tej, której się spodziewaliśmy.

Krok 6) Utwórz klasę uruchamiającą testy, aby uruchomić sparametryzowany test:

Klasa TestRunner przekazująca AirthematicTest.class do JUnitCore.runClasses i błędy drukowania

Code Wyjaśnienie:

  • Code Linia 8: Deklarowanie głównej metody klasy Test, która będzie uruchamiać nasze JUnit test.
  • Code Linia 9: Wykonywanie przypadków testowych przy użyciu JUnitCore.runClasses, który przyjmuje nazwę klasy testowej jako parametr (w naszym przykładzie używamy AirthematicTest.class).
  • Code Linia 11: Przetwarzanie wyniku za pomocą pętli for i drukowanie błędnego wyniku.
  • Code Linia 13: Drukowanie pomyślnego wyniku.

Wyjście:

Oto wynik, który pokazuje pomyślny test bez żadnych błędów trace, jak podano poniżej. Należy zauważyć, że JUnit widok wyświetla jeden wpis na wiersz danych zamiast pojedynczego testu:

Eclipse JUnit Wyświetl raportowanie Uruchomiono 5/5 z 0 błędami i 0 awariami dla sparametryzowanej klasy

Zobacz wynik na konsoli, który pokazuje dodanie dwóch liczb:

Eclipse konsola drukująca jedną sumę Numbers wiersz dla każdego z pięciu wierszy parametrów

Testy parametryczne w JUnit 5 z @ParameterizedTest

Powyższy przykład został napisany dla JUnit 4. JUnit 5 (Jupiter) całkowicie usuwa model runnera, więc @RunWith(Parameterized.class), konstruktor danych i pola instancji znikają. JUnit Kod pokazany powyżej nie jest przestarzały: nadal działa bez zmian na JUnit Platforma korzysta z silnika vintage. Nowe testy są jednak zazwyczaj pisane z tagiem @ParameterizedTest.

Wymagane są dwie zależności: junit-jupiter-api dla adnotacji testowych i junit-jowisz-parametry dla wsparcia parametryzowanego. Bez drugiego artefaktu adnotacje źródłowe w ogóle się nie rozwiążą.

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 oferuje kilka źródeł argumentów, a wybór właściwego zależy od kształtu danych:

Adnotacja źródłowa Kieszonkowe dzieci Użyj tego, kiedy
@ValueSource Pojedyncza kolumna literałów Test przyjmuje dokładnie jeden argument
@CsvSource Wiersze w linii rozdzielone przecinkami Małe tabele liczb i ciągów znaków można łatwo odczytać w pliku
@CsvFileSource Wiersze odczytane z pliku CSV w ścieżce klasy testowej Zestaw danych jest duży lub jest utrzymywany poza kodem
@MethodSource Statyczna fabryka zwracająca strumień argumentów Potrzebne są obiekty rzeczywiste, wartości obliczeniowe lub dane losowe
@EnumSource Stałe wyliczenia Każda wartość wyliczeniowa musi zostać wykonana

Dwie zasady zaskakują większość początkujących. Adnotacja źródłowa umieszczona w prostej metodzie @Test jest automatycznie ignorowana, więc metoda musi zawierać @ParameterizedTest. Ponadto pusta wartość bez cudzysłowu w @CsvSource jest odczytywana jako null, a pusta wartość w cudzysłowie jest odczytywana jako pusty ciąg znaków.

JUnit Cztery adnotacje użyte w tym artykule mapują się na Jupiter w następujący sposób: @RunWith(Parameterized.class) staje się @ParameterizedTest z adnotacją źródłową, @Parameters staje się @MethodSource lub @CsvSource, a @Before staje się @BeforeEach. Pełna lista znajduje się w JUnit adnotacje poradnik.

Zalety i ograniczenia testów parametryzowanych

Parametryzacja nie jest darmowa. Eliminuje duplikaty, ale również ogranicza sposób pisania testu, dlatego warto poznać obie strony przed konwersją istniejącego pakietu.

Zalety

  • Less powielanie: Jedna z metod polega na zastąpieniu bloku niemal identycznych instrukcji assert, jak pokazano na pierwszym zrzucie ekranu w tym artykule.
  • Tańsze ubezpieczenie: Dodanie przypadku brzegowego kosztuje jeden dodatkowy wiersz danych zamiast zupełnie nowego walizka testowa Metoda.
  • Dokładne raportowanie: Każda iteracja jest raportowana osobno, więc JUnit widok identyfikuje dokładnie wiersz, który uległ awarii, zamiast identyfikować wszystkie agregowane błędy.
  • Dane scentralizowane: Dane wejściowe znajdują się w pojedynczej metodzie i mogą być później przeniesione do pliku CSV lub fabryki bez konieczności modyfikowania asercji.

Ograniczenia

  • Jedna forma stwierdzenia: Każdy wiersz uruchamia te same asercje, więc scenariusz wymagający innych sprawdzeń nadal wymaga własnej metody testowania.
  • Zakres na poziomie klasy w JUnit 4: Obiekt uruchamiający parametryzuje całą klasę, więc niezwiązane z nią metody @Test w tej klasie również są uruchamiane raz na wiersz.
  • Nieczytelne raporty: Bez szablonu nazwy błędy pojawiają się jako testAirthematicTest[3], co nie mówi nic o danych, które uległy uszkodzeniu.
  • Duże dane wbudowane: Duże tablice wykluczają logikę testową; zamiast tego przenieś je do fabryki @CsvFileSource lub @MethodSource.

Typowe błędy w JUnit Testy sparametryzowane

Większość sparametryzowanych błędów to błędy inicjalizacji zgłaszane przed wykonaniem choćby jednej asercji. Poniższa tabela zawiera listę najczęściej pojawiających się komunikatów i czynników je wywołujących.

Treść wiadomości Spowodować Fix
Klasa testowa powinna mieć dokładnie jeden konstruktor publiczny Klasa nie deklaruje konstruktora publicznego lub deklaruje dwa konstruktory publiczne Zachowaj jeden konstruktor publiczny, którego parametry odpowiadają kolumnom danych
Brak publicznej metody parametrów statycznych w klasie Metoda @Parameters nie jest publiczna statyczna lub zwraca niewłaściwy typ Zadeklaruj ją jako publiczną statyczną kolekcję i zwróć Arrays.asList(…)
IllegalArgumentException: nieprawidłowa liczba argumentów Wiersz jest szerszy lub węższy niż lista parametrów konstruktora Ustaw każdą tablicę w kolekcji na taką samą szerokość jak konstruktor
Błąd konfiguracji: brak dostawcy argumentów Test Jupitera zawiera @ParameterizedTest bez adnotacji źródłowej Dodaj @ValueSource, @CsvSource, @CsvFileSource, @MethodSource lub @EnumSource
Wydaje się, że adnotacja źródłowa nic nie robi Metoda jest oznaczona adnotacją @Test zamiast @ParameterizedTest Zastąp @Test przez @ParameterizedTest i zaimportuj junit-jupiter-params

Kolejną pułapką jest współdzielony stan. Ponieważ JUnit Buduje nową instancję dla każdego wiersza, wszystko, co znajduje się w polu statycznym, przetrwa każdą iterację, a wartość zapisana przez wiersz [0] może dyskretnie zmienić wynik wiersza [4]. Zachowaj stan dla każdego wiersza w polach instancji i zresetuj współdzielone zasoby w metodzie @Before lub @BeforeEach. Ogólne wskazówki dotyczące izolowania testów znajdują się w testów jednostkowych poradnik.

FAQ

TestNG dostarcza wiersze poprzez metodę @DataProvider, do której odwołuje się każda metoda testowa, więc niepowiązane testy w klasie nie pozostają naruszone. JUnit 4 parametryzuje całą klasę za pomocą jej programu uruchamiającego. JUnit 5 wypełnia tę lukę za pomocą metody @ParameterizedTest.

Tak. JUnit 4 akceptuje @Parameters(name = “{index}: sum({0},{1})={2}”), a Jupiter akceptuje @ParameterizedTest(name = “…”). Symbole zastępcze są zastępowane w czasie wykonywania, więc raport o błędzie podaje nazwę wiersza, który powoduje błąd, zamiast wyświetlać tylko indeks.

Tak. JUnit 4 obsługuje @Parameter(0) i @Parameter(1) w publicznych polach niestatycznych, a klasa wówczas opiera się na domyślnym konstruktorze. Połączenie wstrzykiwania pola z konstruktorem danych powoduje błąd „dokładnie jeden konstruktor publiczny”.

JUnit 4 potrzebuje tylko artefaktu junit, ponieważ sparametryzowany runner jest w nim zawarty. JUnit Wersja 5 wymaga junit-jupiter-params obok junit-jupiter-api; bez tego artefaktu @ParameterizedTest i wszystkie adnotacje źródłowe nie zostaną rozwiązane.

Jupiter udostępnia @CsvFileSource(resources = “/data.csv”, numLinesToSkip = 1), które odczytuje wiersze ze ścieżki klas testowych. JUnit 4 nie ma wbudowanego odpowiednika, więc metoda @Parameters musi otworzyć i przeanalizować sam plik przed zwróceniem kolekcji.

In JUnit 4. Może, ale runner parametryzuje całą klasę, więc każda metoda jest uruchamiana raz dla każdego wiersza danych. Jupiter parametryzuje poszczególne metody, więc zwykłe metody @Test w tej samej klasie nadal są uruchamiane dokładnie raz.

Asystenci AI odczytują sygnaturę metody i proponują wiersze graniczne, takie jak dane wejściowe zerowe, ujemne, maksymalne i przepełnienia, których często brakuje w ręcznie napisanej tabeli. Revprzeglądaj każdy wygenerowany oczekiwany wynik, ponieważ model może wygenerować wiarygodny wiersz z błędną odpowiedzią.

Drugi pilot GitHub szybko wytwarza rusztowanie, ale często miesza JUnit 4 i Jupiter importuje, a czasami pozostawia adnotację źródłową w prostej metodzie @Test. Sprawdź importy przed uruchomieniem pakietu.

Podsumuj ten post następująco: