En rask JUnit vs TestNG-sammenligning
1. Oversikt
JUnit og TestNG er utvilsomt de to mest populære rammene for enhetstesting i Java-økosystemet. Mens JUnit inspirerer TestNG selv, gir den sine særegne egenskaper, og i motsetning til JUnit fungerer den for funksjonelle og høyere nivåer av testing.
I dette innlegget, Vi vil diskutere og sammenligne disse rammene ved å dekke deres funksjoner og vanlige brukssaker.
2. Testoppsett
Mens vi skriver testtilfeller, må vi ofte utføre noen konfigurasjons- eller initialiseringsinstruksjoner før testutførelser, og også litt opprydding etter at testene er fullført. La oss evaluere disse i begge rammene.
JUnit tilbyr initialisering og opprydding på to nivåer, før og etter hver metode og klasse. Vi har @BeforeEach, @EfterEach merknader på metodenivå og @BeforeAll og @Tross alt på klassenivå:
public class SummationServiceTest {private static List numbers; @BeforeAll public static void initialize () {numbers = new ArrayList (); } @AfterAll offentlig statisk ugyldighet tearDown () {tall = null; } @BeforeEach public void runBeforeEachTest () {numbers.add (1); numbers.add (2); numbers.add (3); } @AfterEach public void runAfterEachTest () {numbers.clear (); } @Test offentlig ugyldighet gittNumbers_sumEquals_thenCorrect () {int sum = numbers.stream (). Reduser (0, Integer :: sum); assertEquals (6, sum); }}
Merk at dette eksemplet bruker JUnit 5. I den forrige JUnit 4-versjonen, må vi bruke @Før og @Etter merknader som tilsvarer @BeforeEach og @EfterEach. Like måte, @BeforeAll og @Tross alt er erstatninger for JUnit 4 @BeforeClass og @Etter timen.
I likhet med JUnit, TestNG gir også initialisering og opprydding på metode- og klassenivå. Samtidig som @BeforeClass og @Etter timen forbli den samme på klassenivå, merknadsnivåkommentarene er @Før metoden og @AfterMethod:
@BeforeClass public void initialize () {numbers = new ArrayList (); } @AfterClass public void tearDown () {numbers = null; } @BeforeMethod offentlig ugyldig runBeforeEachTest () {numbers.add (1); numbers.add (2); numbers.add (3); } @AfterMethod offentlig ugyldig runAfterEachTest () {numbers.clear (); }
TestNG tilbyr også, @BeforeSuite, @AfterSuite, @BeforeGroup og @AfterGroup merknader, for konfigurasjoner på suite- og gruppenivå:
@BeforeGroups ("positive_tests") offentlig ugyldig runBeforeEachGroup () {numbers.add (1); numbers.add (2); numbers.add (3); } @AfterGroups ("negative_tests") offentlig ugyldig runAfterEachGroup () {numbers.clear (); }
Vi kan også bruke @BeforeTest og @AfterTest hvis vi trenger konfigurasjon før eller etter testtilfeller inkludert i tag i TestNG XML-konfigurasjonsfil:
Merk at erklæringen om @BeforeClass og @Etter timen metoden må være statisk i JUnit. Til sammenligning har ikke testNG-metodedeklarasjonen disse begrensningene.
3. Ignorer tester
Begge rammene støtter å ignorere testsaker, selv om de gjør det ganske annerledes. JUnit tilbyr @Overse kommentar:
@Ignorer @Test offentlig ugyldighet givenNumbers_sumEquals_thenCorrect () {int sum = numbers.stream (). Reduser (0, Heltall :: sum); Assert.assertEquals (6, sum); }
mens TestNG bruker @Test med en parameter “aktivert” med en boolsk verdi ekte eller falsk:
@Test (aktivert = falsk) offentlig ugyldighet givenNumbers_sumEquals_thenCorrect () {int sum = numbers.stream.reduce (0, Integer :: sum); Assert.assertEquals (6, sum); }
4. Kjører tester sammen
Å kjøre tester sammen som en samling er mulig i begge JUnit og TestNG, men de gjør det på forskjellige måter.
Vi kan bruke @RunWith,@SelectPackages, og @SelectClasses merknader for å gruppere testsaker og kjøre dem som en suite i JUnit 5. En suite er en samling testsaker som vi kan gruppere sammen og kjøre som en enkelt test.
Hvis vi ønsker å gruppere testtilfeller av forskjellige pakker for å kjøre sammen i en Suite vi trenger @SelectPackages kommentar:
@RunWith (JUnitPlatform.class) @SelectPackages ({"org.baeldung.java.suite.childpackage1", "org.baeldung.java.suite.childpackage2"}) offentlig klasse SelectPackagesSuiteUnitTest {}
Hvis vi vil at spesifikke testklasser skal kjøre sammen, JUnit 5 gir fleksibilitet gjennom @SelectClasses:
@RunWith (JUnitPlatform.class) @SelectClasses ({Class1UnitTest.class, Class2UnitTest.class}) offentlig klasse SelectClassesSuiteUnitTest {}
Tidligere brukt JUnit 4, vi oppnådde gruppering og kjørte flere tester sammen ved hjelp av @Suite kommentar:
@RunWith (Suite.class) @ Suite.SuiteClasses ({RegistrationTest.class, SignInTest.class}) offentlig klasse SuiteTest {}
I TestNG kan vi gruppere tester ved hjelp av en XML-fil:
Dette indikerer Registreringstest og SignInTest vil løpe sammen.
Bortsett fra grupperingskurs, kan TestNG også gruppere metoder ved å bruke @Test (grupper = ”groupName”) kommentar:
@Test (grupper = "regresjon") offentlig tomrom gittNegativeNumber_sumLessthanZero_thenCorrect () {int sum = numbers.stream (). Reduser (0, Heltall :: sum); Assert.assertTrue (sum <0); }
La oss bruke en XML til å utføre gruppene:
Dette vil utføre testmetoden merket med gruppen regresjon.
5. Testing av unntak
Funksjonen for testing av unntak ved bruk av merknader er tilgjengelig i både JUnit og TestNG.
La oss først lage en klasse med en metode som gir et unntak:
public class Calculator {public double divide (double a, double b) {if (b == 0) {throw new DivideByZeroException ("Divider kan ikke være lik null!"); } returnere a / b; }}
I JUnit 5 vi kan bruke hevder Kaster API for å teste unntak:
@Test offentlig ugyldig nårDividerIsZero_thenDivideByZeroExceptionIsThrown () {Calculator calculator = new Calculator (); assertThrows (DivideByZeroException.class, () -> calculator.divide (10, 0)); }
I JUnit 4, vi kan oppnå dette ved å bruke @Test (forventet = DivideByZeroException.class) over test-API-et.
Og med TestNG kan vi også implementere det samme:
@Test (expectExceptions = ArithmeticException.class) public void givenNumber_whenThrowsException_thenCorrect () {int i = 1/0; }
Denne funksjonen innebærer hvilket unntak som kastes fra et stykke kode, det er en del av en test.
6. Parameteriserte tester
Parameteriserte enhetstester er nyttige for å teste den samme koden under flere forhold. Ved hjelp av parametriserte enhetstester kan vi sette opp en testmetode som innhenter data fra noen datakilde. Hovedideen er å gjøre enhetstestmetoden gjenbrukbar og å teste med et annet sett med innganger.
I JUnit 5, har vi fordelen av at testmetoder bruker dataargumenter direkte fra den konfigurerte kilden. Som standard gir JUnit 5 noen få kilde kommentarer som:
- @ValueSource: vi kan bruke dette med en rekke verdier av typen Kort, byte, int, lang, flyt, dobbel, røye, og Streng:
@ParameterizedTest @ValueSource (strings = {"Hello", "World"}) ugyldig givenString_TestNullOrNot (Stringord) {assertNotNull (ord); }
- @EnumSource - passerer Enum konstanter som parametere for testmetoden:
@ParameterizedTest @EnumSource (verdi = PizzaDeliveryStrategy.class, names = {"EXPRESS", "NORMAL"}) ugyldig givenEnum_TestContainsOrNot (PizzaDeliveryStrategy timeUnit) {assertTrue (EnumSet.of (PizzaDeliveryStrategy. Power. ; }
- @MethodSource - svurderer eksterne metoder som genererer strømmer:
statisk Stream wordDataProvider () {return Stream.of ("foo", "bar"); } @ParameterizedTest @MethodSource ("wordDataProvider") ugyldig givenMethodSource_TestInputStream (Strengargument) {assertNotNull (argument); }
- @CsvSource - bruker CSV-verdier som kilde for parametrene:
@ParameterizedTest @CsvSource ({"1, Car", "2, House", "3, Train"}) ugyldig gittCSVSource_TestContent (int id, Strengord) {assertNotNull (id); assertNotNull (ord); }
På samme måte har vi andre kilder som @CsvFileSource hvis vi trenger å lese en CSV-fil fra classpath og @ArgumentSource for å spesifisere en tilpasset, gjenbrukbar Argumenter - leverandør.
I JUnit 4, må testklassen kommenteres @RunWith for å gjøre det til en parameterisert klasse og @Parameter for å bruke betegne parameterverdiene for enhetstest.
I TestNG kan vi parametrere tester ved hjelp av @Parameter eller @DataProvider kommentarer. Når du bruker XML-filen, kan du kommentere testmetoden med @Parameter:
@Test @Parameters ({"value", "isEven"}) public void givenNumberFromXML_ifEvenCheckOK_thenCorrect (int value, boolean isEven) {Assert.assertEquals (isEven, value% 2 == 0); }
og oppgi dataene i XML-filen:
Selv om bruk av informasjon i XML-filen er enkel og nyttig, i noen tilfeller må du kanskje oppgi mer komplekse data.
For dette kan vi bruke @DataProvider kommentar som lar oss kartlegge komplekse parametertyper for testmetoder.
Her er et eksempel på bruk @DataProvider for primitive datatyper:
@DataProvider (name = "numbers") offentlig statisk Object [] [] evenNumbers () {return new Object [] [] {{1, false}, {2, true}, {4, true}}; } @Test (dataProvider = "numbers") offentlig ugyldighet givenNumberFromDataProvider_ifEvenCheckOK_thenCorrect (heltall, forventet boolsk) {Assert.assertEquals (forventet, antall% 2 == 0); }
Og @DataProvider for gjenstander:
@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, sant)}}; }
På samme måte kan spesielle objekter som skal testes opprettes og returneres ved hjelp av dataleverandør. Det er nyttig når du integrerer med rammer som Spring.
Legg merke til at i TestNG siden @DataProvider metoden trenger ikke å være statisk, vi kan bruke flere dataleverandørmetoder i samme testklasse.
7. Test tidsavbrudd
Tidsbestemt tester betyr at en testsak skal mislykkes hvis utførelsen ikke er fullført innen en bestemt spesifisert periode. Både JUnit og TestNG support avviklet tester. I JUnit 5 vi kan skrive en timeout-test som:
@Test offentlig ugyldig gittExecution_takeMoreTime_thenFail () kaster InterruptedException {Assertions.assertTimeout (Duration.ofMillis (1000), () -> Thread.sleep (10000)); }
I JUnit 4 og TestNG kan vi den samme testen ved å bruke @Test (timeout = 1000)
@Test (timeOut = 1000) offentlig ugyldig givenExecution_takeMoreTime_thenFail () {while (true); }
8. Avhengige tester
TestNG støtter avhengighetstesting. Dette betyr at i et sett med testmetoder, hvis den første testen mislykkes, vil alle påfølgende avhengige tester bli hoppet over, ikke merket som mislykket som i tilfelle for JUnit.
La oss se på et scenario der vi trenger å validere e-post, og hvis det lykkes, fortsetter vi å logge på:
@Test offentlig ugyldighet gittEmail_ifValid_thenTrue () {boolean valid = email.contains ("@"); Assert.assertEquals (valid, true); } @Test (dependsOnMethods = {"givenEmail_ifValid_thenTrue"}) offentlig ugyldig givenValidEmail_whenLoggedIn_thenTrue () {LOGGER.info ("E-post {} gyldig >> pålogging", e-post); }
9. Bestilling av testutførelse
Det er ingen definert implisitt rekkefølge hvor testmetoder blir utført i JUnit 4 eller TestNG. Metodene blir nettopp påkalt som returnert av Java Reflection API. Siden JUnit 4 bruker den en mer deterministisk, men ikke forutsigbar rekkefølge.
For å ha mer kontroll, vil vi kommentere testklassen med @FixMethodOrder kommentar og nevn en metodesorterer:
@FixMethodOrder (MethodSorters.NAME_ASCENDING) offentlig klasse SortedTests {@Test public void a_givenString_whenChangedtoInt_thenTrue () {assertTrue (Integer.valueOf ("10") instance of Integer); } @Test offentlig ugyldig b_givenInt_whenChangedtoString_thenTrue () {assertTrue (String.valueOf (10) instance of String); }}
De MethodSorters.NAME_ASCENDING parameter sorterer metodene etter metodenavnet er leksikografisk rekkefølge. Bortsett fra denne sorteringen, har vi det MethodSorter.DEFAULT og MethodSorter.JVM også.
Mens TestNG også gir et par måter å ha kontroll i rekkefølgen på utførelse av testmetode. Vi tilbyr prioritet parameter i @Test kommentar:
@Test (prioritet = 1) offentlig tomrom gittString_whenChangedToInt_thenCorrect () {Assert.assertTrue (Integer.valueOf ("10") instance of Integer); } @Test (prioritet = 2) offentlig ugyldighet gittInt_whenChangedToString_thenCorrect () {Assert.assertTrue (String.valueOf (23) instance of String); }
Legg merke til at prioritet påkaller testmetoder basert på prioritet, men ikke garanterer at tester i ett nivå er fullført før de påkaller neste prioritetsnivå.
Noen ganger mens vi skriver funksjonelle testtilfeller i TestNG, kan vi ha en gjensidig avhengig test der rekkefølgen på utførelsen må være den samme for hver testkjøring. For å oppnå det, bør vi bruke avhenger av metoder parameter til @Test kommentar som vi så i den forrige delen.
10. Navn på egendefinert test
Når vi kjører en test, blir testklassen og navnet på testmetoden som standard skrevet ut i konsoll eller IDE. JUnit 5 gir en unik funksjon der vi kan nevne tilpassede beskrivende navn for klasse- og testmetoder ved hjelp av @DisplayName kommentar.
Denne kommentaren gir ingen testfordeler, men den gir også lett å lese og forstå testresultatene for en ikke-teknisk person:
@ParameterizedTest @ValueSource (strings = {"Hello", "World"}) @DisplayName ("Test Method to check at the inputs are not nullable") ugyldig givenString_TestNullOrNot (Strengord) {assertNotNull (word); }
Når vi kjører testen, vil utgangen vise visningsnavnet i stedet for metodenavnet.
Akkurat nå, i TestNG det er ingen måte å oppgi et tilpasset navn.
11. Konklusjon
Både JUnit og TestNG er moderne verktøy for testing i Java-økosystemet.
I denne artikkelen så vi raskt på forskjellige måter å skrive tester på med hver av disse to testrammene.
Implementeringen av alle kodebitene finnes i TestNG og junit-5 Github-prosjektet.