Java >> Java tutorial >  >> Tag >> maven

Kører JUnit-tests parallelt med Maven

1. Introduktion

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

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

2. Maven afhængigheder

Lad os begynde med at importere de nødvendige afhængigheder. Vi skal bruge JUnit 4.7 eller nyere sammen med Surefire 2.16 eller nyere:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
</plugin>

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

  • Multithreading i en enkelt JVM-proces
  • Fordeling af flere JVM-processer

3. Kørsel af parallelle tests

For at køre en test parallelt bør vi bruge en testløber, der udvider org.junit.runners.ParentRunner .

Men selv test, der ikke erklærer en eksplicit testløber, fungerer, da standardløberen udvider denne klasse.

Dernæst, for at demonstrere parallel testudførelse, vil vi bruge en testpakke med to testklasser, der hver har nogle få metoder. Faktisk ville enhver standardimplementering af en JUnit-testpakke gøre det.

3.1. Brug af Parallel Parameter

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 parallelitet.

De mulige værdier er:

  • metoder –  kører testmetoder i separate tråde
  • klasser – kører testklasser i separate tråde
  • classesAndMethods – 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 til metoder
  • alle – kører suiter, klasser samt metoder i separate tråde

I vores eksempel bruger vi alle :

<configuration>
    <parallel>all</parallel>
</configuration>

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

Bruger threadCount som definerer det maksimale antal tråde, Surefire vil oprette:

<threadCount>10</threadCount>

Eller brug useUnlimitedThreads parameter, hvor der oprettes én tråd pr. CPU-kerne:

<useUnlimitedThreads>true</useUnlimitedThreads>

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

<perCoreThreadCount>true</perCoreThreadCount>

3.2. Brug af trådtællebegrænsninger

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

Lad os kombinere disse parametre med threadCount fra den tidligere konfiguration: 

<threadCountSuites>2</threadCountSuites>
<threadCountClasses>2</threadCountClasses>
<threadCountMethods>6</threadCountMethods>

Da vi brugte alle i parallel,  vi har defineret trådantal 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å skal vi bare sørge for threadCountthreadCountClasses threadCountSuites.

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

Vi kan også anvende begrænsninger for trådantal i sådanne tilfælde:

<useUnlimitedThreads>true</useUnlimitedThreads>
<threadCountClasses>2</threadCountClasses>

3.3. Indstilling af timeouts

Nogle gange kan vi være nødt til at sikre, at testudførelsen er tidsbegrænset.

For at gøre det kan vi bruge parallelTestTimeoutForcedInSeconds  parameter. Dette vil afbryde aktuelt kørende tråde og vil ikke udføre nogen af ​​de tråde i kø, efter timeout er udløbet:

<parallelTestTimeoutForcedInSeconds>5</parallelTestTimeoutForcedInSeconds>

En anden mulighed er at bruge parallelTestTimeoutInSeconds .

I dette tilfælde vil kun de tråde, der står i kø, blive stoppet i at udføre:

<parallelTestTimeoutInSeconds>3.5</parallelTestTimeoutInSeconds>

Ikke desto mindre vil testene med begge muligheder ende med en fejlmeddelelse, når timeouten er udløbet.

3.4. Forbehold

Surefire kalder statiske metoder, der er kommenteret med @Parameters , @BeforeClass og @AfterClass i forældretråden. Sørg derfor for at tjekke for potentielle hukommelsesinkonsekvenser eller raceforhold, før du kører test parallelt.

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

4. Testudførelse i Multi-Module Maven-projekter

Indtil nu har vi fokuseret på at køre tests parallelt i 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 som bygger moduler parallelt . Dette kan gøres på to måder.

Vi kan enten angive det nøjagtige antal tråde, der skal bruges under opbygningen af ​​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 samt opbygge eksekveringstider.

5. Forking JVM'er

Med den parallelle testkørsel via den parallelle  mulighed, samtidigt sker inde i JVM-processen ved hjælp af tråde .

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

For at forhindre samtidighedsproblemer på trådniveau, tilbyder Surefire en anden parallel testudførelsestilstand:forking og procesniveau-samtidighed . Ideen med forgrenede processer er faktisk ret simpel. I stedet for at skabe flere tråde og fordele testmetoderne imellem dem, skaber surefire nye processer og udfører den samme distribution.

Da der ikke er nogen delt hukommelse mellem forskellige processer, vil vi ikke lide af disse subtile samtidighedsfejl. Det kommer selvfølgelig på bekostning af mere hukommelsesforbrug og lidt mindre hastighed.

Uanset hvad, for at aktivere forling skal vi bare bruge forkCount  egenskab og indstil den til enhver positiv værdi:

<forkCount>3</forkCount>

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

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

<forkCount>2.5C</forkCount>

Derefter kan Surefire i en to-core maskine skabe højst fem gafler til parallel testudførelse.

Som standard vil Surefire genbruge de oprettede gafler til andre tests . Men hvis vi indstiller genbrugeForks  egenskab til false , vil det ødelægge hver gaffel efter at have kørt en testklasse.

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

6. Konklusion

For at opsummere startede vi med at aktivere flertrådsadfærd og definere graden af ​​parallelitet ved hjælp af parallel parameter. Efterfølgende anvendte vi begrænsninger på antallet af tråde, Surefire skulle oprette. Senere sætter vi timeout-parametre for at kontrollere testudførelsestider.

Til sidst så vi på, hvordan vi kan reducere byggeudførelsestider og derfor teste eksekveringstider i multi-modul Maven-projekter.

Som altid er koden præsenteret her tilgængelig på GitHub.


Java tag