Rýchle porovnanie JUnit vs TestNG

1. Prehľad

JUnit a TestNG sú nepochybne dva najpopulárnejšie rámce na testovanie jednotiek v ekosystéme Java. Zatiaľ čo JUnit inšpiruje samotný TestNG, poskytuje svoje charakteristické črty a na rozdiel od JUnit funguje pre funkčné a vyššie úrovne testovania.

V tomto príspevku budeme diskutovať a porovnávať tieto rámce pokrytím ich funkcií a bežných prípadov použitia.

2. Vyskúšajte nastavenie

Pri písaní testovacích prípadov často musíme pred vykonaním testu vykonať určité pokyny na konfiguráciu alebo inicializáciu a po dokončení testov tiež nejaké vyčistenie. Zhodnoťme ich v oboch rámcoch.

JUnit ponúka inicializáciu a čistenie na dvoch úrovniach, pred a po každej metóde a triede. Máme @BeforeEach, @AfterEach anotácie na úrovni metódy a @BeforeAll a @Po všetkom na úrovni triedy:

public class SummationServiceTest {private static List numbers; @BeforeAll verejné static void initialize () {numbers = new ArrayList (); } @AfterAll verejné statické void tearDown () {numbers = null; } @BeforeEach public void runBeforeEachTest () {numbers.add (1); čísla.pridat (2); čísla.pridat (3); } @AfterEach public void runAfterEachTest () {numbers.clear (); } @Test public void givenNumbers_sumEquals_thenCorrect () {int sum = numbers.stream (). Reduce (0, Integer :: sum); assertEquals (6, suma); }}

Upozorňujeme, že tento príklad používa JUnit 5. V predchádzajúcej verzii JUnit 4 by sme museli používať znak @ Predtým a @ Potom anotácie, ktoré sú ekvivalentné s @BeforeEach a @AfterEach. Podobne, @BeforeAll a @Po všetkom sú náhradami za JUnit 4 @BeforeClass a @Po hodine.

Podobne ako JUnit, TestNG tiež poskytuje inicializáciu a čistenie na úrovni metód a tried. Zatiaľ čo @BeforeClass a @Po hodine rovnaké na úrovni triedy, anotácie na úrovni metódy sú @Pred metódou a @AfterMethod:

@BeforeClass public void initialize () {numbers = new ArrayList (); } @AfterClass public void tearDown () {numbers = null; } @BeforeMethod public void runBeforeEachTest () {numbers.add (1); čísla.pridat (2); čísla.pridat (3); } @AfterMethod public void runAfterEachTest () {numbers.clear (); }

TestNG tiež ponúka, @BeforeSuite, @AfterSuite, @BeforeGroup a @AfterGroup anotácie, pre konfigurácie na úrovni balíka a skupiny:

@BeforeGroups ("positive_tests") public void runBeforeEachGroup () {numbers.add (1); čísla.pridat (2); čísla.pridat (3); } @AfterGroups ("negative_tests") public void runAfterEachGroup () {numbers.clear (); }

Môžeme tiež použiť @BeforeTest a @AfterTest ak potrebujeme akúkoľvek konfiguráciu pred alebo po testovacích prípadoch zahrnutých v značka v konfiguračnom súbore XML TestNG:

Upozorňujeme, že vyhlásenie z @BeforeClass a @Po hodine metóda musí byť v JUnit statická. Pre porovnanie, deklarácia metódy TestNG tieto obmedzenia nemá.

3. Ignorovanie testov

Oba rámce podporujú ignorovanie testovacích prípadov, aj keď to robia úplne inak. JUnit ponúka @Ignorovať anotácia:

@Ignore @Test public void givenNumbers_sumEquals_thenCorrect () {int sum = numbers.stream (). Reduce (0, Integer :: sum); Assert.assertEquals (6, suma); }

zatiaľ čo TestNG používa @Test s parametrom „povolený“ s boolovskou hodnotou pravda alebo nepravdivé:

@Test (enabled = false) public void givenNumbers_sumEquals_thenCorrect () {int sum = numbers.stream.reduce (0, Integer :: sum); Assert.assertEquals (6, suma); }

4. Bežné testy spolu

Spúšťanie testov spoločne ako kolekcie je možné v oboch JUnit a TestNG, ale robia to rôznymi spôsobmi.

Môžeme použiť @RunWith,@SelectPackagesa @SelectClasses anotácie na zoskupenie testovacích prípadov a ich spustenie ako súboru v systéme Windows JUnit 5. Sada je kolekcia testovacích prípadov, ktoré môžeme zoskupiť a spustiť ako jeden test.

Ak chceme zoskupiť testovacie prípady rôznych balíkov tak, aby bežali spolu v rámci a Suita potrebujeme @SelectPackages anotácia:

@RunWith (JUnitPlatform.class) @SelectPackages ({"org.baeldung.java.suite.childpackage1", "org.baeldung.java.suite.childpackage2"}) verejná trieda SelectPackagesSuiteUnitTest {}

Ak chceme, aby konkrétne testovacie triedy prebiehali spoločne, JUnit 5 poskytuje flexibilitu prostredníctvom @SelectClasses:

@RunWith (JUnitPlatform.class) @SelectClasses ({Class1UnitTest.class, Class2UnitTest.class}) verejná trieda SelectClassesSuiteUnitTest {}

Predtým pomocou JUnit 4, pomocou zoskupenia sme dosiahli zoskupenie a spustenie viacerých testov @Suite anotácia:

@RunWith (Suite.class) @ Suite.SuiteClasses ({RegistrationTest.class, SignInTest.class}) verejná trieda SuiteTest {}

V TestNG môžeme testy zoskupiť pomocou súboru XML:

To naznačuje Registračný test a SignInTest pobeží spolu.

Okrem zoskupovania tried môže TestNG zoskupovať aj metódy pomocou znaku @Test (groups = ”groupName”) anotácia:

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

Na vykonanie skupín použijeme XML:

Týmto sa vykoná testovacia metóda označená skupinou regresia.

5. Testovanie výnimiek

Funkcia testovania výnimiek pomocou anotácií je k dispozícii v JUnit aj TestNG.

Najprv vytvorme triedu metódou, ktorá vyvolá výnimku:

public class Calculator {public double divide (double a, double b) {if (b == 0) {throw new DivideByZeroException ("Divider cannot be equal to zero!"); } návrat a / b; }}

V JUnit 5 môžeme použiť tvrdiťHodí API na testovanie výnimiek:

@ Test public void whenDividerIsZero_thenDivideByZeroExceptionIsThrown () {Calculator calculator = new Calculator (); assertThrows (DivideByZeroException.class, () -> calculator.divide (10, 0)); }

V JUnit 4, môžeme to dosiahnuť použitím @Test (očakáva sa = DivideByZeroException.class) cez testovacie API.

A s TestNG môžeme implementovať aj to isté:

@Test (expectExceptions = ArithmeticException.class) public void givenNumber_whenThrowsException_thenCorrect () {int i = 1/0; }

Táto funkcia naznačuje, aká výnimka je vyvolaná z časti kódu, ktorá je súčasťou testu.

6. Parametrizované testy

Parametrizované jednotkové testy sú užitočné pri testovaní toho istého kódu za niekoľkých podmienok. Pomocou parametrizovaných jednotkových testov môžeme nastaviť testovaciu metódu, ktorá získava údaje z nejakého zdroja údajov. Hlavnou myšlienkou je urobiť testovaciu metódu jednotky znovu použiteľnou a testovať s inou sadou vstupov.

V JUnit 5, máme výhodu v tom, že testovacie metódy využívajú dátové argumenty priamo z nakonfigurovaného zdroja. V predvolenom nastavení poskytuje JUnit 5 niekoľko zdroj anotácie ako:

  • @ValueSource: môžeme to použiť s poľom hodnôt typu Krátke, Byte, Int, Dlhé, Float, Double, Char, a Reťazec:
@ParameterizedTest @ValueSource (strings = {"Hello", "World"}) void givenString_TestNullOrNot (reťazcové slovo) {assertNotNull (slovo); }
  • @EnumSource - prihrávky Enum konštanty ako parametre k skúšobnej metóde:
@ParameterizedTest @EnumSource (value = PizzaDeliveryStrategy.class, names = {"EXPRESS", "NORMAL"}) void givenEnum_TestContainsOrNot (PizzaDeliveryStrategy timeUnit) {assertTrue (EnumSet.of (PizzaDeliveryStrategy.EXStrategy.EXS) ; }
  • @MethodSource - strhodnotí externé metódy generovania prúdov:
statický prúd wordDataProvider () {návrat Stream.of ("foo", "bar"); } @ParameterizedTest @MethodSource ("wordDataProvider") void givenMethodSource_TestInputStream (argument reťazca) {assertNotNull (argument); }
  • @CsvSource - používa hodnoty CSV ako zdroj parametrov:
@ParameterizedTest @CsvSource ({"1, Car", "2, House", "3, Train"}) void givenCSVSource_TestContent (int id, string name) {assertNotNull (id); assertNotNull (slovo); }

Podobne máme aj ďalšie zdroje ako @CsvFileSource ak potrebujeme prečítať súbor CSV z classpath a @ArgumentSource určiť vlastné, opakovane použiteľné Argumenty Poskytovateľ.

V JUnit 4, testovacia trieda musí byť anotovaná @RunWith aby z toho bola parametrizovaná trieda a @Parameter použiť na označenie hodnoty parametra pre jednotkový test.

V TestNG môžeme parametrizovať testy pomocou @Parameter alebo @DataProvider anotácie. Pri použití súboru XML anotujte testovaciu metódu pomocou @Parameter:

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

a poskytnite údaje v súbore XML:

Aj keď je použitie informácií v súbore XML jednoduché a užitočné, v niektorých prípadoch bude pravdepodobne potrebné poskytnúť zložitejšie údaje.

Na tento účel môžeme použiť @DataProvider anotácia, ktorá nám umožňuje mapovať zložité typy parametrov pre testovacie metódy.

Tu je príklad použitia @DataProvider pre primitívne dátové typy:

@DataProvider (name = "numbers") public static Object [] [] evenNumbers () {return new Object [] [] {{1, false}, {2, true}, {4, true}}; } @Test (dataProvider = "numbers") public void givenNumberFromDataProvider_ifEvenCheckOK_thenCorrect (celé číslo, očakáva sa boolean) {Assert.assertEquals (očakávané, číslo% 2 == 0); }

A @DataProvider pre objekty:

@Test (dataProvider = "numbersObject") public void 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 EvenNumber (4, true)}}; }

Rovnakým spôsobom je možné pomocou poskytovateľa údajov vytvoriť a vrátiť všetky konkrétne objekty, ktoré sa majú testovať. Je to užitočné pri integrácii s rámcami ako Spring.

Všimnite si, že v TestNG od @DataProvider metóda nemusí byť statická, v jednej testovacej triede môžeme použiť viac metód poskytovateľa údajov.

7. Časový limit testu

Testy s časovým limitom znamenajú, že testovací prípad by mal zlyhať, ak sa vykonanie nedokončí v stanovenej lehote. Podpora JUnit aj TestNG vypršala. V JUnit 5 môžeme napísať test časového limitu ako:

@Test public void givenExecution_takeMoreTime_thenFail () hodí InterruptedException {Assertions.assertTimeout (Duration.ofMillis (1000), () -> Thread.sleep (10 000)); }

V JUnit 4 a TestNG môžeme urobiť rovnaký test pomocou @Test (časový limit = 1 000)

@Test (timeOut = 1000) public void givenExecution_takeMoreTime_thenFail () {while (true); }

8. Závislé testy

TestNG podporuje testovanie závislostí. To znamená, že ak v súbore testovacích metód zlyhá počiatočný test, budú všetky nasledujúce závislé testy preskočené a nebudú označené ako neúspešné ako v prípade JUnit.

Pozrime sa na scenár, kde musíme overiť e-mail, a ak bude úspešný, pristúpime k prihláseniu:

@Test public void givenEmail_ifValid_thenTrue () {boolean valid = email.contains ("@"); Assert.assertEquals (platné, pravdivé); } @Test (dependsOnMethods = {"givenEmail_ifValid_thenTrue"}) verejné neplatné givenValidEmail_whenLoggedIn_thenTrue () {LOGGER.info ("E-mail {} platné >> prihlásenie", e-mail); }

9. Poradie vykonania testu

Nie je definované žiadne implicitné poradie, v ktorom sa testovacie metódy vykonajú v JUnit 4 alebo TestNG. Metódy sú vyvolané ako vrátené rozhraním Java Reflection API. Od JUnit 4 používa deterministickejšie, ale nepredvídateľné poradie.

Aby sme mali väčšiu kontrolu, urobíme anotáciu testovacej triedy @FixMethodOrder anotáciu a spomenúť triedič metód:

@FixMethodOrder (MethodSorters.NAME_ASCENDING) verejná trieda SortedTests {@Test public void a_givenString_whenChangedtoInt_thenTrue () {assertTrue (Integer.valueOf ("10") inštancia celého čísla); } @Test public void b_givenInt_whenChangedtoString_thenTrue () {assertTrue (String.valueOf (10) instanceof String); }}

The MethodSorters.NAME_ASCENDING parameter triedi metódy podľa názvu metódy v lexikografickom poradí. Okrem tohto triediča máme MethodSorter.DEFAULT aj MethodSorter.JVM.

Zatiaľ čo TestNG poskytuje aj niekoľko spôsobov, ako mať kontrolu v poradí vykonania testovacej metódy. Poskytujeme prioritou parameter v @Test anotácia:

@Test (priority = 1) public void givenString_whenChangedToInt_thenCorrect () {Assert.assertTrue (Integer.valueOf ("10") inštancia Integer); } @Test (priorita = 2) public void givenInt_whenChangedToString_thenCorrect () {Assert.assertTrue (String.valueOf (23) instanceof String); }

Všimnite si, že priorita vyvoláva testovacie metódy založené na priorite, ale nezaručuje dokončenie testov na jednej úrovni pred vyvolaním ďalšej úrovne priority.

Niekedy pri písaní funkčných testovacích prípadov v TestNG môžeme mať vzájomne závislý test, v ktorom musí byť poradie vykonania rovnaké pre každý test. Aby sme to dosiahli, mali by sme použiť dependOnMethods parameter do @Test anotáciu, ako sme videli v predchádzajúcej časti.

10. Názov vlastného testu

Štandardne sa pri každom spustení testu vytlačí testovacia trieda a názov testovacej metódy v konzole alebo IDE. JUnit 5 poskytuje jedinečnú funkciu, keď môžeme spomenúť vlastné popisné názvy pre triedy a testovacie metódy, ktoré sa používajú @Zobraziť meno anotácia.

Táto anotácia neposkytuje žiadne výhody pri testovaní, ale prináša ľahko čitateľné a zrozumiteľné výsledky testov aj pre netechnickú osobu:

@ParameterizedTest @ValueSource (strings = {"Hello", "World"}) @DisplayName ("Testovacia metóda na kontrolu, či vstupy nemajú povolenú hodnotu") void givenString_TestNullOrNot (reťazcové slovo) {assertNotNull (slovo); }

Kedykoľvek spustíme test, výstup zobrazí namiesto názvu metódy zobrazovaný názov.

Práve teraz, v TestNG neexistuje spôsob, ako uviesť vlastný názov.

11. Záver

JUnit aj TestNG sú moderné nástroje na testovanie v ekosystéme Java.

V tomto článku sme sa rýchlo pozreli na rôzne spôsoby písania testov s každým z týchto dvoch testovacích rámcov.

Implementáciu všetkých fragmentov kódu nájdete v projektoch TestNG a junit-5 Github.