Kører JUnit-tests parallelt med Maven

1. Introduktion

Selvom udførelse af tests serielt fungerer fint det meste af tiden, vil vi måske parallelisere dem for at fremskynde tingene.

I denne vejledning vil vi dække, hvordan man paralleliserer tests ved hjælp af JUnit og Mavens Surefire Plugin. Først kører vi alle test i en enkelt JVM-proces, så prøver vi det med et projekt med flere moduler.

2. Maven-afhængigheder

Lad os begynde med at importere de krævede afhængigheder. Vi bliver nødt til at bruge JUnit 4.7 eller senere sammen med Surefire 2.16 eller nyere:

 junit junit 4.12 test 
 org.apache.maven.plugins maven-surefire-plugin 2.22.0 

I en nøddeskal giver Surefire to måder at udføre tests parallelt på:

  • Multithreading i en enkelt JVM-proces
  • Gafler flere JVM-processer

3. Kørsel af parallelle tests

For at køre en test parallelt, skal vi bruge en testløber, der strækker sig org.junit.runners.ParentRunner.

Selv test, der ikke erklærer et eksplicit testløber, fungerer dog, da standardløberen udvider denne klasse.

For at demonstrere parallel testudførelse bruger vi derefter en testpakke med to testklasser, der hver har et par metoder. Faktisk ville enhver standardimplementering af en JUnit-testpakke gøre det.

3.1. Brug af parallelparameter

Lad os først aktivere parallel adfærd i Surefire ved hjælp af parallel parameter. Den angiver det granularitetsniveau, hvor vi gerne vil anvende parallelisme.

De mulige værdier er:

  • metoder - kører testmetoder i separate tråde
  • klasser - kører testklasser i separate tråde
  • klasser og metoder - kører klasser og metoder i separate tråde
  • suiter - kører suiter parallelt
  • suitesAndClasses - kører suiter og klasser i separate tråde
  • suitesAndMethods - opretter separate tråde til klasser og metoder
  • alle - kører suiter, klasser såvel som metoder i separate tråde

I vores eksempel bruger vi alle:

 alle 

For det andet, lad os definere det samlede antal tråde, vi ønsker, at Surefire skal oprette. Vi kan gøre det på to måder:

Ved brug af threadCount der definerer det maksimale antal tråde, Surefire opretter:

10

Eller ved hjælp af useUnlimitedThreads parameter, hvor der oprettes en tråd pr. CPU-kerne:

rigtigt

Som standard, threadCount er pr. CPU-kerne. Vi kan bruge parameteren perCoreThreadCount for at aktivere eller deaktivere denne adfærd:

rigtigt

3.2. Brug af trådtællingsbegrænsninger

Lad os sige, at vi vil definere antallet af tråde, der skal oprettes på metode-, klasse- og suite-niveau. Vi kan gøre dette med threadCountMethods, threadCountClasses og threadCountSuites parametre.

Lad os kombinere disse parametre med threadCount fra den foregående konfiguration:

2 2 6

Da vi brugte alle i parallel, Vi har defineret trådtællinger for metoder, suiter og klasser. Det er dog ikke obligatorisk at definere bladparameteren. Surefire udleder antallet af tråde, der skal bruges, hvis bladparametre udelades.

For eksempel hvis threadCountMethods er udeladt, så er vi bare nødt til at sikre os threadCount >threadCountClasses + threadCountSuites.

Nogle gange vil vi måske begrænse antallet af tråde oprettet til klasser eller suiter eller metoder, selvom vi bruger et ubegrænset antal tråde.

Vi kan også anvende begrænsninger i antallet af tråde i sådanne tilfælde:

sandt 2

3.3. Indstilling af timeouts

Nogle gange kan det være nødvendigt at sikre, at testudførelsen er tidsbegrænset.

For at gøre det kan vi bruge parallelTestTimeoutForcedInSeconds parameter. Dette afbryder aktuelle kørende tråde og udfører ikke nogen af ​​de tråde, der er i kø, efter at timeout er forløbet:

5

En anden mulighed er at bruge parallelTestTimeoutInSeconds.

I dette tilfælde stoppes kun trådene i kø fra at udføre:

3.5

Ikke desto mindre slutter testene med begge indstillinger med en fejlmeddelelse, når timeoutet er udløbet.

3.4. Advarsler

Surefire kalder statiske metoder kommenteret med @Parameters, @BeforeClassog @Efter skole i overordnet tråd. Sørg således for at kontrollere for potentielle hukommelsesinkonsekvenser eller løbetilstand, før du kører tests parallelt.

Test, der muterer delt tilstand, er bestemt ikke gode kandidater til at køre parallelt.

4. Testudførelse i multimodul Maven-projekter

Indtil nu har vi fokuseret på at køre tests parallelt inden for et Maven-modul.

Men lad os sige, at vi har flere moduler i et Maven-projekt. Da disse moduler er bygget sekventielt, udføres testene for hvert modul også sekventielt.

Vi kan ændre denne standardadfærd ved at bruge Mavens -T parameter, der bygger moduler parallelt. Dette kan gøres på to måder.

Vi kan enten specificere det nøjagtige antal tråde, der skal bruges, mens vi bygger projektet:

mvn -T 4 surefire: test

Eller brug den bærbare version og angiv antallet af tråde, der skal oprettes pr. CPU-kerne:

mvn -T 1C surefire: test

Uanset hvad kan vi fremskynde tests såvel som opbygningstider.

5. Forking JVM'er

Med den parallelle testudførelse via parallel valgmulighed, sker samtidighed inde i JVM-processen ved hjælp af tråde.

Da tråde deler samme hukommelsesplads, kan dette være effektivt med hensyn til hukommelse og hastighed. Vi kan dog støde på uventede løbsbetingelser eller andre subtile samtidige testfejl. Som det viser sig, at dele det samme hukommelsesrum kan være både en velsignelse og en forbandelse.

For at forhindre trådniveau-samtidige problemer giver Surefire en anden parallel testudførelsestilstand: forking og samtidighed på procesniveau. Ideen om forked processer er faktisk ret enkel. I stedet for at gyde flere tråde og distribuere testmetoderne imellem dem opretter surefire nye processer og udfører den samme distribution.

Da der ikke er nogen delt hukommelse mellem forskellige processer, lider vi ikke af disse subtile samtidige bugs. Selvfølgelig kommer dette på bekostning af mere hukommelsesforbrug og lidt mindre hastighed.

Alligevel, For at muliggøre gaffel er vi bare nødt til at bruge forkCount ejendom og sæt den til en hvilken som helst positiv værdi:

3

Her opretter surefire højst tre gafler fra JVM og kører testene i dem. Standardværdien for forkCount er en, hvilket betyder det maven-surefire-plugin opretter en ny JVM-proces til at udføre alle test i et Maven-modul.

Det forkCount egenskab understøtter den samme syntaks som -T. Det vil sige, hvis vi tilføjer C til værdien ganges denne værdi med antallet af tilgængelige CPU-kerner i vores system. For eksempel:

2,5C

Derefter kan Surefire i en maskine med to kerner højst oprette fem gafler til parallel testudførelse.

Som standard, Surefire vil genbruge de oprettede gafler til andre tests. Men hvis vi indstiller genbrugForks ejendom til falsk, det ødelægger hver gaffel efter at have kørt en testklasse.

For at deaktivere gaflen kan vi også indstille forkCount til nul.

6. Konklusion

For at opsummere startede vi med at aktivere adfærd med flere tråde og definere graden af ​​parallelisme ved hjælp af parallel parameter. Derefter anvendte vi begrænsninger på antallet af tråde, som Surefire skulle oprette. Senere indstillede vi timeout-parametre til at kontrollere testudførelsestider.

Endelig så vi på, hvordan vi kan reducere udførelsestider for byggeri og derfor teste udførelsestider i Maven-projekter med flere moduler.

Som altid er koden, der præsenteres her, tilgængelig på GitHub.