Vejledning til dynamiske tests i Junit 5

1. Oversigt

Dynamisk test er en ny programmeringsmodel, der blev introduceret i JUnit 5. I denne artikel vil vi se på, hvad dynamiske tests er, og hvordan man opretter dem.

Hvis du er helt ny med JUnit 5, kan du kontrollere eksemplet på JUnit 5 og vores primære guide.

2. Hvad er en DynamicTest?

Standardtesterne er kommenteret med @Prøve annotation er statiske test, som er fuldt specificeret på kompileringstidspunktet. EN DynamicTest er en test genereret under kørselstid. Disse tests genereres ved en fabriksmetode, der er kommenteret med @TestFabrik kommentar.

EN @TestFabrik metoden skal returnere en Strøm, Kollektion, Iterabel, eller Iterator af DynamicTest tilfælde. At returnere noget andet vil resultere i en JUnitException da de ugyldige returtyper ikke kan detekteres på kompileringstidspunktet. Bortset fra dette, a @TestFabrik metoden kan ikke være statiskc eller privat.

Det DynamicTests udføres forskelligt fra standarden @Prøves og understøtter ikke tilbagekald fra livscyklus. Betydning, den @BeforeEach og @AfterEach metoder kaldes ikke til DynamicTests.

3. Oprettelse DynamicTests

Lad os først se på forskellige måder at skabe DynamicTests.

Eksemplerne her er ikke dynamiske, men de giver et godt udgangspunkt for at skabe virkelig dynamiske.

Vi opretter en Kollektion af DynamicTest:

@TestFactory Collection dynamicTestsWithCollection () {return Arrays.asList (DynamicTest.dynamicTest ("Add test", () -> assertEquals (2, Math.addExact (1, 1))), DynamicTest.dynamicTest ("Multiply Test", ( ) -> assertEquals (4, Math.multiplyExact (2, 2)))); }

Det @TestFabrik metode fortæller JUnit, at dette er en fabrik til oprettelse af dynamiske tests. Som vi kan se, returnerer vi kun en Kollektion af DynamicTest. Hver af de DynamicTest består af to dele, navnet på testen eller displaynavnet og en Eksekverbar.

Outputtet indeholder det displaynavn, som vi har sendt til de dynamiske tests:

Tilføj test (dynamicTestsWithCollection ()) Multiplicer test (dynamicTestsWithCollection ())

Den samme test kan ændres for at returnere en Iterabel, Iterator, eller a Strøm:

@TestFactory Iterable dynamicTestsWithIterable () {return Arrays.asList (DynamicTest.dynamicTest ("Add test", () -> assertEquals (2, Math.addExact (1, 1))), DynamicTest.dynamicTest ("Multiply Test", ( ) -> assertEquals (4, Math.multiplyExact (2, 2)))); } @TestFactory Iterator dynamicTestsWithIterator () {return Arrays.asList (DynamicTest.dynamicTest ("Add test", () -> assertEquals (2, Math.addExact (1, 1))), DynamicTest.dynamicTest ("Multiply Test", () -> assertEquals (4, Math.multiplyExact (2, 2)))) .iterator (); } @TestFactory Stream dynamiske testsFromIntStream () {return IntStream.iterate (0, n -> n + 2) .limit (10) .mapToObj (n -> DynamicTest.dynamicTest ("test" + n, () -> assertTrue (n % 2 == 0))); }

Bemærk, at hvis @TestFabrik returnerer a Strøm, så lukkes den automatisk, når alle testene er udført.

Outputtet vil være stort set det samme som det første eksempel. Det indeholder det visningsnavn, som vi videregiver til den dynamiske test.

4. Oprettelse af en Strøm af DynamicTests

Af hensyn til demonstrationen skal du overveje a DomainNameResolver som returnerer en IP-adresse, når vi videregiver domænenavnet som input.

Af enkelheds skyld skal vi se på det høje niveau skelet af vores fabriksmetode:

@TestFactory Stream dynamiskTestsFromStream () {// eksempel input og output Liste inputList = Arrays.asList ("www.somedomain.com", "www.anotherdomain.com", "www.yetanotherdomain.com"); Liste outputList = Arrays.asList ("154.174.10.56", "211.152.104.132", "178.144.120.156"); // inputgenerator, der genererer input ved hjælp af inputList /*...kode her ... * / // en displaygenerator, der opretter et // andet navn baseret på input /*...koden her ... * / // testudføreren, som faktisk har // logikken til at udføre testsagen /*... kode her ... * / // kombinere alt og returnere en strøm af DynamicTest /*... kode her ... * /}

Der er ikke meget kode relateret til DynamicTest her bortset fra @TestFabrik kommentar, som vi allerede kender.

De to ArrayLists vil blive brugt som input til DomainNameResolver henholdsvis forventet output.

Lad os nu se på inputgeneratoren:

Iterator inputGenerator = inputList.iterator ();

Inputgeneratoren er intet andet end en Iterator af Snor. Det bruger vores inputListe og returnerer domænenavnet en efter en.

Displaynavngeneratoren er ret enkel:

Funktion displayNameGenerator = (input) -> "Løsning:" + input;

Opgaven med en displaynavngenerator er bare at give et displaynavn til testsagen, der skal bruges i JUnit-rapporter eller fanen JUnit i vores IDE.

Her bruger vi bare domænenavnet til at generere unikke navne til hver test. Det er ikke nødvendigt at oprette unikke navne, men det hjælper i tilfælde af fejl. Når du har dette, kan vi fortælle det domænenavn, som testsagen mislykkedes for.

Lad os nu se på den centrale del af vores test - testudførelseskoden:

DomainNameResolver resolver = ny DomainNameResolver (); ThrowingConsumer testExecutor = (input) -> {int id = inputList.indexOf (input); assertEquals (outputList.get (id), resolver.resolveDomain (input)); };

Vi har brugt Kaster forbruger, som er en @FunktionelInterface til skrivning af testsagen. For hver input genereret af datageneratoren henter vi det forventede output fra outputListe og den faktiske output fra en forekomst af DomainNameResolver.

Nu er den sidste del simpelthen at samle alle brikkerne og vende tilbage som en Strøm af DynamicTest:

returner DynamicTest.stream (inputGenerator, displayNameGenerator, testExecutor);

Det er det. Ved at køre testen vises rapporten, der indeholder de navne, der er defineret af vores displaygenerator:

Løsning: www.somedomain.com (dynamicTestsFromStream ()) Løsning: www.anotherdomain.com (dynamicTestsFromStream ()) Løsning: www.yetanotherdomain.com (dynamicTestsFromStream ())

5. Forbedring af DynamicTest Brug af Java 8-funktioner

Testfabrikken, der er skrevet i det foregående afsnit, kan forbedres drastisk ved hjælp af funktionerne i Java 8. Den resulterende kode bliver meget renere og kan skrives i et mindre antal linjer:

@TestFactory Strøm dynamicTestsFromStreamInJava8 () {DomainNameResolver resolver = ny DomainNameResolver (); Liste domænenavne = Arrays.asList ("www.somedomain.com", "www.anotherdomain.com", "www.yetanotherdomain.com"); Liste outputList = Arrays.asList ("154.174.10.56", "211.152.104.132", "178.144.120.156"); returner inputList.stream () .map (dom -> DynamicTest.dynamicTest ("Løsning:" + dom, () -> {int id = inputList.indexOf (dom); assertEquals (outputList.get (id), resolver.resolveDomain) (dom));})); }

Ovenstående kode har samme effekt som den, vi så i det foregående afsnit. Det inputList.stream (). kort () giver strømmen af ​​indgange (inputgenerator). Det første argument til dynamicTest () er vores displaygenerator (“Løsning:” + dom) mens det andet argument, a lambda, er vores testudførelse.

Outputtet vil være det samme som det forrige afsnit.

6. Yderligere eksempel

I dette eksempel udforsker vi yderligere styrken ved de dynamiske tests til at filtrere input baseret på testcases:

@TestFactory Strøm dynamicTestsForEmployeeWorkflows () {Liste inputList = Arrays.asList (ny medarbejder (1, "Fred"), ny medarbejder (2), ny medarbejder (3, "John")); EmployeeDao dao = ny EmployeeDao (); Stream saveEmployeeStream = inputList.stream () .map (emp -> DynamicTest.dynamicTest ("saveEmployee:" + emp.toString (), () -> {Medarbejder returnerede = dao.save (emp.getId ()); assertEquals ( returneret.getId (), emp.getId ());})); Stream saveEmployeeWithFirstNameStream = inputList.stream () .filter (emp ->! Emp.getFirstName (). IsEmpty ()) .map (emp -> DynamicTest.dynamicTest ("saveEmployeeWithName" + emp.toString (), () -> { Medarbejder returnerede = dao.save (emp.getId (), emp.getFirstName ()); assertEquals (returneret.getId (), emp.getId ()); assertEquals (returneret.getFirstName (), emp.getFirstName ()); })); returner Stream.concat (saveEmployeeStream, saveEmployeeWithFirstNameStream); }

Det gem (lang) metoden behøver kun Medarbejder-ID. Derfor bruger den alle de Medarbejder tilfælde. Det gem (lang, streng) metode behov fornavn bortset fra Medarbejder-ID. Derfor filtrerer den ud Medarbejder tilfælde uden fornavn.

Endelig kombinerer vi begge streams og returnerer alle testene som en enkelt Strøm.

Lad os nu se på output:

saveEmployee: Medarbejder [id = 1, firstName = Fred] (dynamicTestsForEmployeeWorkflows ()) saveEmemedarbejder: Medarbejder [id = 2, firstName =] (dynamicTestsForEmployeeWorkflows ()) saveEmemedarbejder: Medarbejder [id = 3, firstName = John] (dynamicTestsForEmemed saveEmployeeWithNameEmployee [id = 1, firstName = Fred] (dynamicTestsForEmployeeWorkflows ()) saveEmployeeWithNameEmployee [id = 3, firstName = John] (dynamicTestsForEmployeeWorkflows ())

7. Konklusion

De parametriserede test kan erstatte mange af eksemplerne i denne artikel. Imidlertid adskiller de dynamiske test sig fra de parametriserede tests, da de understøtter fuld testlivscyklus, mens parametriserede tests ikke gør det.

Desuden giver dynamiske tests større fleksibilitet med hensyn til, hvordan input genereres, og hvordan testene udføres.

JUnit 5 foretrækker udvidelser frem for funktioner. Som et resultat er hovedformålet med dynamiske tests at give et udvidelsespunkt for tredjepartsrammer eller udvidelser.

Du kan læse mere om andre funktioner i JUnit 5 i vores artikel om gentagne tests i JUnit 5.

Glem ikke at tjekke den fulde kildekode til denne artikel på GitHub.


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