JUnit-tests parallel met Maven uitvoeren

1. Inleiding

Hoewel het serieel uitvoeren van tests meestal prima werkt, willen we ze misschien evenwijdig laten lopen om de zaken te versnellen.

In deze zelfstudie bespreken we hoe u tests kunt parallelliseren met JUnit en Maven's Surefire Plugin. Eerst voeren we alle tests uit in een enkel JVM-proces, daarna proberen we het met een project met meerdere modules.

2. Maven afhankelijkheden

Laten we beginnen met het importeren van de vereiste afhankelijkheden. We hebben JUnit 4.7 of hoger nodig samen met Surefire 2.16 of hoger:

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

Kort samengevat biedt Surefire twee manieren om tests parallel uit te voeren:

  • Multithreading binnen één JVM-proces
  • Forking van meerdere JVM-processen

3. Parallelle tests uitvoeren

Om een ​​test parallel uit te voeren, moeten we een testrunner gebruiken die uitschuift org.junit.runners.ParentRunner.

Maar zelfs tests die geen expliciete testrunner verklaren, werken, aangezien de standaardrunner deze klasse uitbreidt.

Om vervolgens de parallelle testuitvoering te demonstreren, gebruiken we een testsuite met twee testklassen die elk een paar methoden hebben. In feite zou elke standaardimplementatie van een JUnit-testsuite voldoende zijn.

3.1. Parallelle parameter gebruiken

Laten we eerst parallel gedrag in Surefire inschakelen met behulp van de parallel parameter. Het geeft aan op welk niveau van granulariteit we parallellisme willen toepassen.

De mogelijke waarden zijn:

  • methoden - voert testmethoden uit in afzonderlijke threads
  • klassen - voert testklassen uit in afzonderlijke threads
  • classesAndMethods - voert klassen en methoden uit in afzonderlijke threads
  • suites - loopt suites parallel
  • suitesAndClasses - voert suites en klassen uit in afzonderlijke threads
  • suitesAndMethods - maakt aparte threads voor klassen en voor methoden
  • alle - voert suites, klassen en methoden uit in afzonderlijke threads

In ons voorbeeld gebruiken we alle:

 alle 

Ten tweede, laten we het totale aantal threads definiëren dat we door Surefire willen laten maken. We kunnen dat op twee manieren doen:

Gebruik makend van draad tellen wat het maximale aantal threads definieert dat Surefire zal creëren:

10

Of gebruikend useUnlimitedThreads parameter waarbij één thread wordt gemaakt per CPU-kern:

waar

Standaard, draad tellen is per CPU-kern. We kunnen de parameter gebruiken perCoreThreadCount om dit gedrag in of uit te schakelen:

waar

3.2. Beperkingen van het aantal threads gebruiken

Laten we nu zeggen dat we het aantal threads willen definiëren dat moet worden gemaakt op methode-, klasse- en suite-niveau. We kunnen dit doen met de threadCountMethods, threadCountClasses en threadCountSuites parameters.

Laten we deze parameters combineren met draad tellen van de vorige configuratie:

2 2 6

Sinds we gebruikten alle in parallel, we hebben het aantal threads voor methoden, suites en klassen gedefinieerd. Het is echter niet verplicht om de leaf-parameter te definiëren. Surefire leidt het aantal threads af dat moet worden gebruikt in het geval bladparameters worden weggelaten.

Als threadCountMethods is weggelaten, dan moeten we er alleen voor zorgen draad tellen >threadCountClasses + threadCountSuites.

Soms willen we het aantal threads dat voor klassen of suites of methoden wordt gemaakt, beperken, zelfs als we een onbeperkt aantal threads gebruiken.

We kunnen in dergelijke gevallen ook beperkingen voor het aantal threads toepassen:

waar 2

3.3. Time-outs instellen

Soms moeten we ervoor zorgen dat de testuitvoering tijdgebonden is.

Om dat te doen kunnen we de parallelTestTimeoutForcedInSeconds parameter. Dit onderbreekt momenteel lopende threads en zal geen van de in de wachtrij geplaatste threads uitvoeren nadat de time-out is verstreken:

5

Een andere optie is om te gebruiken parallelTestTimeoutInSeconds.

In dit geval worden alleen de threads in de wachtrij gestopt met uitvoeren:

3.5

Niettemin zullen bij beide opties de tests eindigen met een foutmelding wanneer de time-out is verstreken.

3.4. Waarschuwingen

Surefire roept statische methoden aan die zijn geannoteerd met @Parameters, @Voor klas, en @Na de les in de bovenliggende thread. Controleer dus op mogelijke geheugeninconsistenties of racecondities voordat u tests parallel uitvoert.

Tests die de gedeelde status muteren, zijn ook zeker geen goede kandidaten om parallel te lopen.

4. Testuitvoering in Maven-projecten met meerdere modules

Tot nu toe hebben we ons gericht op het parallel uitvoeren van tests binnen een Maven-module.

Maar laten we zeggen dat we meerdere modules hebben in een Maven-project. Omdat deze modules opeenvolgend worden gebouwd, worden de tests voor elke module ook opeenvolgend uitgevoerd.

We kunnen dit standaardgedrag wijzigen door Maven's te gebruiken -T parameter die modules parallel bouwt. Dit kan op twee manieren worden gedaan.

We kunnen ofwel het exacte aantal threads specificeren dat moet worden gebruikt tijdens het bouwen van het project:

mvn -T 4 trefzeker: test

Of gebruik de draagbare versie en specificeer het aantal threads dat per CPU-kern moet worden gemaakt:

mvn -T 1C trefzeker: test

Hoe dan ook, we kunnen tests versnellen en uitvoeringstijden opbouwen.

5. Forking JVM's

Met de parallelle testuitvoering via de parallel optie, vindt gelijktijdigheid plaats binnen het JVM-proces met behulp van threads.

Omdat threads dezelfde geheugenruimte delen, kan dit efficiënt zijn in termen van geheugen en snelheid. We kunnen echter onverwachte race-omstandigheden of andere subtiele gelijktijdigheid-gerelateerde testfouten tegenkomen. Het blijkt dat het delen van dezelfde geheugenruimte zowel een zegen als een vloek kan zijn.

Om gelijktijdigheidsproblemen op threadniveau te voorkomen, biedt Surefire een andere parallelle testuitvoeringsmodus: forking en gelijktijdigheid op procesniveau. Het idee van gevorkte processen is eigenlijk vrij eenvoudig. In plaats van meerdere threads te spawnen en de testmethoden tussen hen te verdelen, creëert surefire nieuwe processen en voert dezelfde distributie uit.

Omdat er geen gedeeld geheugen is tussen verschillende processen, zullen we geen last hebben van die subtiele concurrency-bugs. Dit gaat natuurlijk ten koste van meer geheugengebruik en iets minder snelheid.

In ieder geval, om forking mogelijk te maken, hoeven we alleen de forkCount eigenschap en stel deze in op een willekeurige positieve waarde:

3

Hier zal surefire maximaal drie forks van de JVM maken en de tests daarin uitvoeren. De standaardwaarde voor forkCount is er één, wat betekent dat maven-surefire-plugin maakt één nieuw JVM-proces aan om alle tests in één Maven-module uit te voeren.

De forkCount eigenschap ondersteunt dezelfde syntaxis als -T. Dat wil zeggen, als we de C aan de waarde, wordt die waarde vermenigvuldigd met het aantal beschikbare CPU-kernen in ons systeem. Bijvoorbeeld:

2,5C

Vervolgens kan Surefire in een machine met twee kernen maximaal vijf vorken maken voor parallelle testuitvoering.

Standaard, Surefire zal de gemaakte vorken hergebruiken voor andere tests. Als we echter de hergebruik Forken eigendom aan false, het zal elke vork vernietigen na het uitvoeren van een testklasse.

Om de forking uit te schakelen, kunnen we ook de forkCount tot nul.

6. Conclusie

Samenvattend, we zijn begonnen door multi-threaded gedrag mogelijk te maken en de mate van parallellisme te definiëren met behulp van de parallel parameter. Vervolgens hebben we beperkingen toegepast op het aantal threads dat Surefire zou moeten maken. Later hebben we time-outparameters ingesteld om de testuitvoeringstijden te regelen.

Ten slotte hebben we gekeken hoe we de uitvoeringstijden van builds en dus de uitvoeringstijden van tests kunnen verkorten in Maven-projecten met meerdere modules.

Zoals altijd is de hier gepresenteerde code beschikbaar op GitHub.


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