Brza usporedba JUnit-a i TestNG-a

1. Pregled

JUnit i TestNG nesumnjivo su dva najpopularnija okvira za jedinstveno testiranje u Java ekosustavu. Iako JUnit nadahnjuje samog TestNG-a, pruža njegove prepoznatljive značajke, a za razliku od JUnit-a, radi za funkcionalne i više razine testiranja.

U ovom postu, raspravit ćemo i usporediti ove okvire pokrivajući njihove značajke i slučajeve uobičajene upotrebe.

2. Postavljanje testa

Dok pišemo test slučajeve, često moramo izvršiti neke upute za konfiguraciju ili inicijalizaciju prije izvršavanja testa, a također i neko čišćenje nakon završetka testova. Procijenimo ih u oba okvira.

JUnit nudi inicijalizaciju i čišćenje na dvije razine, prije i poslije svake metode i klase. Imamo @BeforeEach, @AfterEach bilješke na razini metode i @BeforeAll i @Nakon svega na razini razreda:

javna klasa SummationServiceTest {privatni statički brojevi popisa; @BeforeAll javna statička praznina initialize () {numbers = new ArrayList (); } @AfterAll javna statička void tearDown () {brojevi = null; } @BeforeEach javna void runBeforeEachTest () {numbers.add (1); brojevi.doda (2); brojevi.doda (3); } @AfterEach javna void runAfterEachTest () {numbers.clear (); } @Test javna praznina givenNumbers_sumEquals_thenCorrect () {int sum = numbers.stream (). Reduce (0, Integer :: sum); assertEquals (6, zbroj); }}

Imajte na umu da ovaj primjer koristi JUnit 5. U prethodnoj verziji JUnit 4 trebali bismo koristiti @Prije i @Nakon napomene koje su ekvivalentne @BeforeEach i @AfterEach. Također, @BeforeAll i @Nakon svega su zamjena za JUnit 4 @BeforeClass i @Nakon nastave.

Slično JUnit, TestNG također omogućuje inicijalizaciju i čišćenje na razini metode i klase. Dok @BeforeClass i @Nakon nastave ostaju isti na razini klase, bilješke na razini metode su @BeforeMethod i @AfterMethod:

@BeforeClass javna praznina initialize () {numbers = new ArrayList (); } @AfterClass javna void tearDown () {brojevi = null; } @BeforeMethod javna void runBeforeEachTest () {numbers.add (1); brojevi.doda (2); brojevi.doda (3); } @AfterMethod javna praznina runAfterEachTest () {numbers.clear (); }

TestNG također nudi, @BeforeSuite, @AfterSuite, @BeforeGroup i @AfterGroup napomene, za konfiguracije na razini apartmana i grupa:

@BeforeGroups ("positive_tests") javna void runBeforeEachGroup () {numbers.add (1); brojevi.doda (2); brojevi.doda (3); } @AfterGroups ("negative_tests") javna void runAfterEachGroup () {numbers.clear (); }

Također, možemo koristiti @BeforeTest i @AfterTest ako trebamo bilo kakvu konfiguraciju prije ili poslije test slučajeva uključenih u oznaka u konfiguracijskoj datoteci TestNG XML:

Imajte na umu da je izjava @BeforeClass i @Nakon nastave metoda mora biti statična u JUnit-u. Za usporedbu, izjava metode TestNG nema ta ograničenja.

3. Zanemarivanje testova

Oba okvira podržavaju ignoriranje test slučajeva, iako to čine sasvim drugačije. JUnit nudi @Zanemariti napomena:

@Ignoriraj @Test javnu prazninu givenNumbers_sumEquals_thenCorrect () {int sum = numbers.stream (). Reduce (0, Integer :: sum); Assert.assertEquals (6, zbroj); }

dok TestNG koristi @Test s parametrom "omogućen" s logičkom vrijednošću pravi ili lažno:

@Test (omogućeno = netačno) javna praznina givenNumbers_sumEquals_thenCorrect () {int sum = numbers.stream.reduce (0, Integer :: sum); Assert.assertEquals (6, zbroj); }

4. Zajedničko izvođenje testova

Zajedničko provođenje testova kao kolekcije moguće je u oba JUNIT i TestNG, ali to rade na različite načine.

Možemo koristiti @RunWith,@SelectPackages, i @SelectClasses napomene za grupiranje testnih slučajeva i njihovo pokretanje kao paket u JUNITET 5. Paket je zbirka test slučajeva koje možemo grupirati i pokrenuti kao jedan test.

Ako želimo grupirati test slučajeve različitih paketa da bi se trčali zajedno unutar a Apartman trebamo @SelectPackages napomena:

@RunWith (JUnitPlatform.class) @SelectPackages ({"org.baeldung.java.suite.childpackage1", "org.baeldung.java.suite.childpackage2"}) javna klasa SelectPackagesSuiteUnitTest {}

Ako želimo da se određene testne satove izvode zajedno, JUNITET 5 pruža fleksibilnost kroz @SelectClasses:

@RunWith (JUnitPlatform.class) @SelectClasses ({Class1UnitTest.class, Class2UnitTest.class}) javna klasa SelectClassesSuiteUnitTest {}

Prije korištenja JUNIT 4, postigli smo grupiranje i izvođenje više testova zajedno koristeći @Suite napomena:

@RunWith (Suite.class) @ Suite.SuiteClasses ({RegistrationTest.class, SignInTest.class}) javna klasa SuiteTest {}

U TestNG-u možemo testove grupirati pomoću XML datoteke:

To ukazuje RegistrationTest i SignInTest trčati će zajedno.

Osim grupiranja klasa, TestNG može grupirati i metode pomoću @Test (groups = "groupName") napomena:

@Test (groups = "regression") javna praznina givenNegativeNumber_sumLessthanZero_thenCorrect () {int sum = numbers.stream (). Reduce (0, Integer :: sum); Assert.assertTrue (zbroj <0); }

Upotrijebimo XML za izvršavanje grupa:

Ovo će izvršiti test metodu označenu s grupom regresija.

5. Testiranje iznimki

Značajka za testiranje iznimaka pomoću napomena dostupna je u JUnit-u i TestNG-u.

Prvo stvorimo klasu s metodom koja donosi izuzetak:

kalkulator javne klase {javni dvostruki razdio (dvostruki a, dvostruki b) {if (b == 0) {baciti novi DivideByZeroException ("Razdjelnik ne može biti jednak nuli!"); } povratak a / b; }}

U JUNITET 5 možemo koristiti assertThrows API za testiranje iznimaka:

@Test public void whenDividerIsZero_thenDivideByZeroExceptionIsThrown () {Kalkulator kalkulatora = novi kalkulator (); assertThrows (DivideByZeroException.class, () -> calculator.divide (10, 0)); }

U JUN 4., to možemo postići pomoću @Test (očekuje se = DivideByZeroException.class) preko test API-ja.

A s TestNG-om možemo implementirati isto:

@Test (očekivaniExceptions = ArithmeticException.class) javna praznina givenNumber_whenThrowsException_thenCorrect () {int i = 1/0; }

Ova značajka podrazumijeva koja se iznimka izbacuje iz dijela koda, to je dio testa.

6. Parametarska ispitivanja

Parametrizirani jedinični testovi korisni su za testiranje istog koda pod nekoliko uvjeta. Uz pomoć parametriziranih jediničnih testova možemo postaviti metodu ispitivanja koja podatke dobiva iz nekog izvora podataka. Glavna ideja je učiniti metodu jediničnog ispitivanja ponovnom uporabom i testirati s različitim skupom ulaza.

U JUNITET 5, imamo prednost u tome što metode ispitivanja troše argumente podataka izravno iz konfiguriranog izvora. Prema zadanim postavkama, JUnit 5 nudi nekoliko izvor napomene poput:

  • @ValueSource: to možemo koristiti s nizom vrijednosti tipa Kratko, Bajt, Int, Dugo, Float, Dvostruko, Char, i Niz:
@ParameterizedTest @ValueSource (strings = {"Hello", "World"}) void givenString_TestNullOrNot (String word) {assertNotNull (word); }
  • @EnumSource - prolazi Enum konstante kao parametri ispitne metode:
@ParameterizedTest @EnumSource (value = PizzaDeliveryStrategy.class, names = {"EXPRESS", "NORMAL"}) void givenEnum_TestContainsOrNot (PizzaDeliveryStrategy timeUnit) {assertTrue (EnumSet.of (PizzaDeliyPRES, PizzaDeliveryStra, PizzaDeliveryStra. ; }
  • @MethodSource - strprocjenjuje vanjske metode generiranja tokova:
static Stream wordDataProvider () {return Stream.of ("foo", "bar"); } @ParameterizedTest @MethodSource ("wordDataProvider") void givenMethodSource_TestInputStream (argument niza) {assertNotNull (argument); }
  • @CsvSource - koristi CSV vrijednosti kao izvor parametara:
@ParameterizedTest @CsvSource ({"1, Car", "2, House", "3, Train"}) void givenCSVSource_TestContent (int id, String word) {assertNotNull (id); assertNotNull (riječ); }

Slično tome, imamo i druge izvore poput @CsvFileSource ako trebamo pročitati CSV datoteku s classpath i @ArgumentSource da odredite prilagođenu, višekratnu upotrebu ArgumentsProvider.

U JUNIT 4, test klasa mora biti označena @RunWith da bi to bila parametrizirana klasa i @Parametar za upotrebu za označavanje vrijednosti parametara za jedinično ispitivanje.

U TestNG-u možemo parametrizirati testove pomoću @Parametar ili @DataProvider bilješke. Dok koristite XML datoteku, test metodu označite s @Parametar:

@Test @Parameters ({"value", "isEven"}) javna praznina givenNumberFromXML_ifEvenCheckOK_thenCorrect (int value, boolean isEven) {Assert.assertEquals (isEven, value% 2 == 0); }

i pružite podatke u XML datoteci:

Iako je korištenje podataka u XML datoteci jednostavno i korisno, u nekim će slučajevima možda trebati pružiti složenije podatke.

Za to možemo koristiti @DataProvider napomena koja nam omogućuje mapiranje složenih vrsta parametara za metode ispitivanja.

Evo primjera korištenja @DataProvider za primitivne tipove podataka:

@DataProvider (name = "numbers") javni statički objekt [] [] evenNumbers () {return new Object [] [] {{1, false}, {2, true}, {4, true}}; } @Test (dataProvider = "numbers") javna praznina givenNumberFromDataProvider_ifEvenCheckOK_thenCorrect (cijeli broj, očekuje se logička vrijednost) {Assert.assertEquals (očekuje se, broj% 2 == 0); }

I @DataProvider za objekte:

@Test (dataProvider = "numbersObject") javna praznina givenNumberObjectFromDataProvider_ifEvenCheckOK_thenCorrect (EvenNumber number) {Assert.assertEquals (number.isEven (), number.getValue ()% 2 == 0); } @DataProvider (name = "numbersObject") public Object [] [] parameterProvider () {return new Object [] [] {{new EvenNumber (1, false)}, {new EvenNumber (2, true)}, {new Parni broj (4, istina)}}; }

Na isti način, bilo koji određeni objekti koji će se testirati mogu se stvoriti i vratiti pomoću davatelja podataka. Korisno je prilikom integracije s okvirima poput Springa.

Primijetite da je u TestNG-u od @DataProvider metoda ne mora biti statična, možemo koristiti više metoda davatelja podataka u istoj testnoj klasi.

7. Istek vremena isteka

Isteklo je vrijeme testova, test slučaj bi trebao uspjeti ako izvršenje nije dovršeno u određenom određenom roku. I JUnit i TestNG podržavaju ispitivanja s vremenskim ograničenjem. U JUNITET 5 možemo napisati test vremenskog ograničenja kao:

@Test javna praznina givenExecution_takeMoreTime_thenFail () baca InterruptedException {Assertions.assertTimeout (Duration.ofMillis (1000), () -> Thread.sleep (10000)); }

U JUNIT 4 i TestNG možemo isti test pomoću @Test (timeout = 1000)

@Test (timeOut = 1000) javna praznina givenExecution_takeMoreTime_thenFail () {while (true); }

8. Ovisni testovi

TestNG podržava testiranje ovisnosti. To znači da u skupu metoda ispitivanja, ako početno ispitivanje ne uspije, svi sljedeći ovisni testovi će se preskočiti, a ne označiti kao neuspjeli kao u slučaju JUnit.

Pogledajmo scenarij u kojem trebamo provjeriti valjanost e-pošte i ako je uspješan, nastavit ćemo se s prijavom:

@Test javna praznina givenEmail_ifValid_thenTrue () {boolean valid = email.contains ("@"); Assert.assertEquals (valjano, točno); } @Test (zavisiOnMethods = {"givenEmail_ifValid_thenTrue"}) javna praznina givenValidEmail_whenLoggedIn_thenTrue () {LOGGER.info ("Email {} valjana >> prijava", e-pošta); }

9. Redoslijed izvođenja testa

Ne postoji definirani implicitni redoslijed kojim će se metode ispitivanja izvršavati u JUnit 4 ili TestNG. Metode se samo pozivaju kao što ih vraća API Java Reflection. Od JUnit 4 koristi se više deterministički, ali ne i predvidljiv poredak.

Da bismo imali veću kontrolu, označit ćemo testnu klasu s @FixMethodOrder napomena i spomenuti sortirnik metode:

@FixMethodOrder (MethodSorters.NAME_ASCENDING) sortirani testovi javne klase {@Test public void a_givenString_whenChangedtoInt_thenTrue () {assertTrue (Integer.valueOf ("10") instance of Integer); } @Test public void b_givenInt_whenChangedtoString_thenTrue () {assertTrue (String.valueOf (10) instanceof String); }}

The MethodSorters.NAME_ASCENDING parametar sortira metode prema nazivu metode leksikografski je poredak. Osim ove sortirnice, imamo MethodSorter.DEFAULT i MethodSorter.JVM također.

Iako TestNG također nudi nekoliko načina za kontrolu u redoslijedu izvršavanja metode ispitivanja. Pružamo prioritet parametar u @Test napomena:

@Test (prioritet = 1) javna praznina givenString_whenChangedToInt_thenCorrect () {Assert.assertTrue (Integer.valueOf ("10") instanceof Integer); } @Test (prioritet = 2) javna praznina givenInt_whenChangedToString_thenCorrect () {Assert.assertTrue (String.valueOf (23) instanceof String); }

Primijetite da prioritet poziva metode ispitivanja na temelju prioriteta, ali ne jamči da su testovi na jednoj razini dovršeni prije pozivanja sljedeće razine prioriteta.

Ponekad dok pišemo slučajeve funkcionalnih testova u TestNG, mogli bismo imati međuovisni test gdje redoslijed izvršavanja mora biti jednak za svako probno pokretanje. Da bismo to postigli trebali bismo koristiti zavisiOnMethods parametar na @Test napomena kao što smo vidjeli u prethodnom odjeljku.

10. Prilagođeni naziv testa

Prema zadanim postavkama, kad god pokrenemo test, klasa testa i naziv metode ispitivanja ispisuju se u konzoli ili IDE-u. JUNITET 5 pruža jedinstvenu značajku u kojoj možemo spominjati prilagođena opisna imena klasa i metoda ispitivanja pomoću @DisplayName bilješka.

Ova napomena ne pruža nikakve koristi od testiranja, ali donosi i čitanje i razumijevanje rezultata ispitivanja za netehničku osobu:

@ParameterizedTest @ValueSource (strings = {"Hello", "World"}) @DisplayName ("Metoda ispitivanja da se provjeri da li ulazi nisu poništivi") void givenString_TestNullOrNot (riječ u nizu) {assertNotNull (riječ); }

Kad god pokrenemo test, izlaz će prikazati ime za prikaz umjesto imena metode.

Trenutno, u TestNG ne postoji način da se navede prilagođeno ime.

11. Zaključak

I JUnit i TestNG su moderni alati za testiranje u ekosustavu Java.

U ovom smo članku na brzinu pregledali razne načine pisanja testova sa svakim od ova dva okvira za testiranje.

Implementacija svih isječaka koda može se naći u projektu TestNG i junit-5 Github.