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.


$config[zx-auto] not found$config[zx-overlay] not found