并发 JUnit 测试

我有一个很大的 JUnit 测试套件,我很想在其中同时运行所有测试,原因有二:

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

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

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

84250 次浏览

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

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

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

我强烈推荐 TestNG

我正在寻找这个问题的答案,基于这里的答案,以及我在其他地方读到的内容,似乎目前还没有一种简单的开箱即用的方法来并行运行现有的测试。如果有的话,我也没找到。因此,我编写了一个简单的 JUnit Runner 来实现这一点。请随意使用它; 有关 MultiThreadedRunner 类的完整解释和源代码,请参阅 http://falutin.net/2012/12/30/multithreaded-testing-with-junit/。使用这个类,您可以像下面这样对现有的测试类进行注释:

@RunWith(MultiThreadedRunner.class)

您也可以尝试 HavaRunner。它是默认情况下并行运行测试的 JUnit 运行程序。

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

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

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

HavaRunner 已经在两个中等规模的 Java 项目中进行了实战测试。

P: 我是《哈瓦跑步者》的作者,我希望你能给我一些反馈。

显然,在他的职位 “使用 RunnerScheduler 的并发 JUnit 测试”马修卡尔布已经做了实现的并发,可以帮助!

Http://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);
}
}

多个联合行动单位:

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

下面的代码应该达到您的要求,这是从德国的书 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(ParallelSuite.class)改变 @RunWith(Suite.class)

您甚至可以通过直接从该套件而不是前面示例中的 Suite进行扩展,来利用 f.e. WildcardPatternSuite的功能。这使您可以进一步通过任何 @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 核的数量。

我不知道还能不能再简单一些:)