Gelijktijdige JUnit testen

Ik heb een grote JUnit test suite, waar ik zou heel graag alle tests gelijktijdig voor twee redenen:

  • Exploiteren meerdere cores voor de hele test suite sneller
  • Hopelijk sporen een aantal fouten die te wijten zijn aan niet-thread-safe globale objecten

Ik erkennen dat dit dwingt mij om refactor sommige code om het thread-safe, maar ik vind dat een goede zaak 🙂

Wat is de beste manier om JUnit alle proeven tegelijk?

InformationsquelleAutor mikera | 2011-02-11

 

5 Replies
  1. 31

    Bent u vast op JUnit? TestNG biedt een goede multi-thread test van het vak en het is compatibel met JUnit tests (die u nodig hebt om een paar wijzigingen). U kunt bijvoorbeeld het uitvoeren van een test zoals deze:

    @Test(threadPoolSize = 3, invocationCount = 9,  timeOut = 10000)
    public void doSomething() {
    ...
    }

    Dit zou betekenen dat de doSomething() methode worden aangeroepen 9 keer met 3 verschillende draden.

    Ik beveel TestNG.

    • +1 voor TestNG, ik gebruik het voor al mijn thread veiligheid van de tests. Het heeft ook mooie paramaterized tests.
    • Downvoted gevolg van het niet daadwerkelijk beantwoorden van de OPs vraag. Hij verklaart specifiek wil hij zijn hele suite in threads en het uitvoeren van een enkele methode meerdere keren
    • Dit is een verschrikkelijke manier van testen voor thread veiligheid. En per definitie niet een unit tests want het is niet deterministisch. En TestNG bevat een groot aantal bugs. Ik heb vast een aantal van hen de laatste tijd, maar ze doen niet getrokken worden (geen suggestie doet), TestNG is redelijk inactief. In plaats van te proberen de Draad Wever (code.google.com/p/thread-weaver)
  2. 21

    Ik was op zoek naar een antwoord op precies deze vraag, en op basis van de antwoorden hier, en wat ik lees elders, lijkt het alsof er is op dit moment niet een gemakkelijke out-of-the-box manier om het uitvoeren van bestaande tests in parallel met behulp van JUnit. Of als er ik niet vinden. Dus ik schreef een eenvoudig JUnit Loper die doet dat. Gelieve te voelen vrij om het te gebruiken; zie http://falutin.net/2012/12/30/multithreaded-testing-with-junit/ voor een complete uitleg en de broncode van de MultiThreadedRunner klasse. Met deze klasse kan je gewoon annoteren uw bestaande test klasse(n) zoals dit:

    @RunWith(MultiThreadedRunner.class)
    • Heb je een idee over hoe het implementeren van een gelijktijdige Suite loper, en niet alleen een gelijktijdige test runner ? Kan ook nuttig zijn :p
    • Ik heb niet gekeken naar die, Stéphane. Maar het maken van de test runner was zo makkelijk, ik wed dat de suite loper zou niet moeilijk. Als u het probleem oplossen, ik zal graag uw code in mijn post 🙂
  3. 21

    De volgende code moet in het behalen van uw eisen, die afkomstig was uit het duitse boek JUnit Profiwissen die bevat een aantal tips om beide tests spullen in parallel of op het verminderen van de uitvoering van tijd door het gebruik van meerdere cores in plaats van alleen een single core.

    JUnit 4.6 introduceerde een ParallelComputer klasse die de parallelle uitvoering van de tests. Echter, deze functionaliteit is niet voor het publiek toegankelijk zijn tot JUnit 4.7 die voorzag in de mogelijkheid om een aangepaste planning voor de ouder loper.

    public class ParallelScheduler implements RunnerScheduler {
    
        private ExecutorService threadPool = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors());
    
        @Override
        public void schedule(Runnable childStatement) {
            threadPool.submit(childStatement);
        }
    
        @Override
        public void finished() {
            try {
                threadPool.shutdown();
                threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Got interrupted", e);
            }
        }
    }
    
    public class ParallelRunner extends BlockJUnit4ClassRunner {
    
        public ParallelRunner(Class<?> klass) throws InitializationError {
            super(klass);
            setScheduler(new ParallelScheduler());
        }
    }

    Als u nu aantekeningen in een test-klasse met @RunWith(ParallelRunner.class) elke methode wordt uitgevoerd in een eigen thread. Verder, er zijn zoveel actieve threads (alleen) als CPU-cores beschikbaar zijn in de uitvoering van de machine.

    Als meerdere klassen moeten worden uitgevoerd in parallel u kunt een aangepaste suite zoals deze:

    public class ParallelSuite extends Suite {
    
        public ParallelSuite(Class<?> klass, RunnerBuilder builder) 
          throws InitializationError {
            super(klass, builder);
            setScheduler(new ParallelScheduler());
        }
    }

    en wijzig @RunWith(Suite.class) met @RunWith(ParallelSuite.class)

    U kunt zelfs gebruikmaken van de functionaliteit van f.e. de WildcardPatternSuite door de uitbreiding direct van die suite in plaats van Suite zoals in het voorbeeld. Dit kunt u verder filteren unit-tests f.e. door een @Category – een TestSuite die alleen uitgevoerd UnitTest geannoteerde categorieën in parallel kan er als volgt uitzien:

    public interface UnitTest {
    
    }
    
    @RunWith(ParallelSuite.class)
    @SuiteClasses("**/*Test.class")
    @IncludeCategories(UnitTest.class)
    public class UnitTestSuite {
    
    }

    Een eenvoudige test kan nu als volgt uitzien:

    @Category(UnitTest.class)
    @RunWith(MockitoJUnitRunner.class)
    public class SomeClassTest {
    
        @Test
        public void testSomething() {
            ...
        }
    }

    De UnitTestSuite uitvoeren voor elke categorie gevonden in subdirecotries dat eindigt met Test en heeft een @Category(UnitTest.class) bedoeld in parallel – afhankelijk van het aantal CPU cores beschikbaar.

    Ik weet niet zeker of het kan eenvoudiger dan dat 🙂

  4. 6

    Blijkbaar Mathieu Carbou gedaan heeft de uitvoering voor de samenloop dat kon helpt!

    http://java.dzone.com/articles/concurrent-junit-tests

    OneJunit

    @RunWith(ConcurrentJunitRunner.class)
    @Concurrent(threads = 6)
    public final class ATest {
    
        @Test public void test0() throws Throwable { printAndWait(); }
        @Test public void test1() throws Throwable { printAndWait(); }
        @Test public void test2() throws Throwable { printAndWait(); }
        @Test public void test3() throws Throwable { printAndWait(); }
        @Test public void test4() throws Throwable { printAndWait(); }
        @Test public void test5() throws Throwable { printAndWait(); }
        @Test public void test6() throws Throwable { printAndWait(); }
        @Test public void test7() throws Throwable { printAndWait(); }
        @Test public void test8() throws Throwable { printAndWait(); }
        @Test public void test9() throws Throwable { printAndWait(); }
    
        void printAndWait() throws Throwable {
            int w = new Random().nextInt(1000);
            System.out.println(String.format("[%s] %s %s %s",Thread.currentThread().getName(), getClass().getName(), new Throwable       ().getStackTrace()[1].getMethodName(), w));
            Thread.sleep(w);
        }
    }

    Meerdere JUnits:

    @RunWith(ConcurrentSuite.class)
    @Suite.SuiteClasses({ATest.class, ATest2.class, ATest3.class})
    public class MySuite {
    }
    • Dit is vrij aardig, maar lijkt niet te werken met tests die een @ BeforeClass en @ Voordat methoden. Het probeert uit te voeren van de @ BeforeClass methoden voor elke thread in plaats van het gewoon een keer. Voor onze setup, betekent dit dat 5 draden vallen en de wederopbouw van de DB op hetzelfde moment 🙁
    • Een manier om dit op te lossen is het handhaven van een statische verwijzing naar f.e. een AtomicBoolean isInitialized variabele in de test-klasse die is ingesteld na de voor de les methode is een keer uitgevoerd. Eventuele extra aanroepen van de init-methode zal alleen terugkeren als deze boolean variabele is waar. Dit voorkomt dat meerdere threads vallen al geïnitialiseerd gegevens
  5. 1

    U kunt ook proberen HavaRunner. Het is een JUnit loper die loopt tests parallel standaard.

    HavaRunner ook heeft handige suites: u kunt het declareren van een test om lid te worden van een badkamer door het toevoegen van de annotatie @PartOf(YourIntegrationTestSuite.class) naar de klas. Deze aanpak wijkt af van JUnit, waar u verklaart de suite lidmaatschappen in de eigen klasse.

    Daarnaast HavaRunner suites intialise zware objecten, zoals een embedded web application container. HavaRunner dan gaat deze zware object aan de constructor van elke suite lid. Dit elimineert de noodzaak voor de @BeforeClass en @AfterClass aantekeningen, die problematisch zijn, omdat ze de bevordering van onstabiele toestand, die op zijn beurt maakt parallelisation moeilijk.

    Ten slotte, HavaRunner heeft scenario ‘ s – een manier om het uitvoeren van de test tegen verschillende gegevens. Scenario ‘ s voor het verminderen van de noodzaak tot dubbele test code.

    HavaRunner is de strijd getest in twee mid-size Java projecten.

    Ps. Ik ben de auteur van HavaRunner, en ik zou het op prijs stellen uw feedback op.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *