并发JUnit测试

时间:2011-02-11 15:35:13

标签: java multithreading unit-testing concurrency junit

我有一个大型的JUnit测试套件,我非常希望同时运行所有测试,原因有两个:

  • 利用多个核心来更快地运行整个测试套件
  • 希望检测由于非线程安全的全局对象导致的一些错误

我认识到这将迫使我重构一些代码以使其成为线程安全的,但我认为这是一件好事: - )

让JUnit同时运行所有测试的最佳方法是什么?

5 个答案:

答案 0 :(得分:27)

你修复了JUnit吗? TestNG 提供了开箱即用的良好多线程测试,并且与JUnit测试兼容(您需要进行一些更改)。例如,你可以运行这样的测试:

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

这意味着doSomething()方法将被3个不同的线程调用9次。

我强烈推荐 TestNG

答案 1 :(得分:20)

我正在寻找这个问题的答案,并根据这里的答案,以及我在其他地方阅读的内容,看起来好像目前没有一种简单易用的方式来运行现有的测试并行使用JUnit。或者,如果有,我没有找到它。所以我写了一个简单的JUnit Runner来完成它。请随意使用;有关MultiThreadedRunner类的完整说明和源代码,请参阅http://falutin.net/2012/12/30/multithreaded-testing-with-junit/。使用此类,您只需注释现有的测试类,如下所示:

@RunWith(MultiThreadedRunner.class)

答案 2 :(得分:20)

以下代码应该达到您的要求,这些要求来自德语书籍JUnit Profiwissen,其中包含一些并行测试内容或如何通过使用多个内核而不是仅使用单个内核来缩短执行时间的提示。

JUnit 4.6引入了一个ParallelComputer类,它提供了并行执行的测试。但是,在JUnit 4.7提供为父运行者设置自定义调度程序的可能性之前,此功能无法公开访问。

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());
    }
}

如果您现在使用@RunWith(ParallelRunner.class)注释测试类,则每个方法都将在其自己的线程中运行。此外,在执行机器上可以使用CPU核心的活动线程数(仅限)。

如果要并行执行多个类,您可以定义这样的自定义套件:

public class ParallelSuite extends Suite {

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

然后使用@RunWith(Suite.class)

更改@RunWith(ParallelSuite.class)

您甚至可以利用f.e的功能。 WildcardPatternSuite直接从该套件扩展而不是Suite,如前面的示例所示。这使您可以进一步过滤单元测试f.e.任何@Category - 只能并行执行UnitTest注释类别的TestSuite看起来像这样:

public interface UnitTest {

}

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

}

一个简单的测试用例现在看起来像这样:

@Category(UnitTest.class)
@RunWith(MockitoJUnitRunner.class)
public class SomeClassTest {

    @Test
    public void testSomething() {
        ...
    }
}

UnitTestSuite将执行在子目录中找到的每个类,这些类以Test结尾并且并行指定@Category(UnitTest.class) - 具体取决于可用的CPU核心数。

我不确定它是否可以变得简单:)

答案 3 :(得分:4)

显然Mathieu Carbou已经实施了可能有帮助的并发!

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);
    }
}

多个JUnits:

@RunWith(ConcurrentSuite.class)
@Suite.SuiteClasses({ATest.class, ATest2.class, ATest3.class})
public class MySuite {
}

答案 4 :(得分:1)

您也可以尝试HavaRunner。它是一个JUnit运行器,默认情况下并行运行测试。

HavaRunner还有方便的套件:您可以通过在类中添加注释@PartOf(YourIntegrationTestSuite.class)来声明测试成为套件的成员。这种方法与JUnit不同,您可以在套件类中声明套件成员资格。

此外,HavaRunner套件可以初始化重量级对象,例如嵌入式Web应用程序容器。然后HavaRunner将这个重量级的对象传递给每个套件成员的构造函数。这消除了对@BeforeClass@AfterClass注释的需要,因为它们会促进全局可变状态,从而使并行化变得困难。

最后,HavaRunner有一些方案 - 一种针对不同数据运行相同测试的方法。场景减少了复制测试代码的需要。

HavaRunner已经在两个中型Java项目中经过了实战测试。

聚苯乙烯。我是HavaRunner的作者,我很感激您对它的反馈。