在 Maven 构建中并行运行 junit 测试?

Posted

技术标签:

【中文标题】在 Maven 构建中并行运行 junit 测试?【英文标题】:Running junit tests in parallel in a Maven build? 【发布时间】:2010-09-30 05:35:06 【问题描述】:

我正在使用 JUnit 4.4 和 Maven,并且我有大量长期运行的集成测试。

当涉及到并行化测试套件时,有一些解决方案可以让我在单个测试类中并行运行每个测试方法。但所有这些都要求我以一种或另一种方式更改测试。

我真的认为在 X 个线程中并行运行 X 个不同的测试类会是一个更简洁的解决方案。我有数百个测试,所以我并不真正关心单个测试类的线程。

有什么办法吗?

【问题讨论】:

【参考方案1】:

使用maven插件:

<build>
    <plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.7.1</version>
        <configuration>
            <parallel>classes</parallel>
            <threadCount>5</threadCount>
        </configuration>
    </plugin>
    </plugins>
</build>

【讨论】:

如果您使用的是 Junit 4.7 或更高版本,则 surefire 实际上支持。 surefire guide【参考方案2】:

从 junit 4.7 开始,现在可以在不使用 TestNG 的情况下并行运行测试。实际上,从 4.6 开始就有可能,但是在 4.7 中进行了许多修复,这将使其成为一个可行的选择。您还可以使用 spring 运行并行测试,您可以阅读 here

【讨论】:

链接页面显示“对于大多数双核解决方案,使用并行线程运行目前永远不会比运行非线程快”。还是这样吗? 我认为,如果您的测试执行任何 IO,它们仍然会受益。例如,如果您的单元测试更像是集成测试并命中数据库,那么并行运行应该会加快它们的速度。 @Raedwald 不要期望 too 对于简短的非 io 绑定单元测试是我想说的。较新版本的 surefire 也比帖子中描述的 2.5 更好/更有效,因此您可能会得到稍微更好的结果。 你说这是可能的,但你能附上一个链接来解释如何做吗?您的第二个链接是“with spring”,我对此不感兴趣。 @krosenvold 链接?我正在努力寻找一个内置的解决方案。【参考方案3】:

受 JUnit 的实验性 ParallelComputer 跑步者的启发,我构建了自己的 ParallelSuiteParallelParameterized 跑步者。使用这些运行器可以轻松并行化测试套件和参数化测试。

ParallelSuite.java

public class ParallelSuite extends Suite 

    public ParallelSuite(Class<?> klass, RunnerBuilder builder) throws InitializationError 

        super(klass, builder);

        setScheduler(new RunnerScheduler() 

            private final ExecutorService service = Executors.newFixedThreadPool(4);

            public void schedule(Runnable childStatement) 
                service.submit(childStatement);
            

            public void finished() 
                try 
                    service.shutdown();
                    service.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
                 catch (InterruptedException e) 
                    e.printStackTrace(System.err);
                
            
        );
    

ParallelParameterized.java

public class ParallelParameterized extends Parameterized 

    public ParallelParameterized(Class<?> arg0) throws Throwable 

        super(arg0);

        setScheduler(new RunnerScheduler() 

            private final ExecutorService service = Executors.newFixedThreadPool(8);

            public void schedule(Runnable childStatement) 
                service.submit(childStatement);
            

            public void finished() 
                try 
                    service.shutdown();
                    service.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
                 catch (InterruptedException e) 
                    e.printStackTrace(System.err);
                
            
        );
    

用法很简单。只需将 @RunWith 注释值更改为这些 Parallel* 类之一。

@RunWith(ParallelSuite.class)
@SuiteClasses(ATest.class, BTest.class, CTest.class)
public class ABCSuite 

【讨论】:

我可以在不提及其名称的情况下为所有测试类并行运行吗?【参考方案4】:

tempus-fugit 提供了类似的功能,请查看文档了解详细信息。它依赖于 JUnit 4.7,您只需将测试标记为 @RunWith(ConcurrentTestRunner)

干杯

【讨论】:

【参考方案5】:

您可以查看开源库 - Test Load Balancer。它完全符合您的要求 - 并行运行不同的测试类。这在 ant-junit 级别集成,因此您无需更改测试。我是图书馆的作者之一。

另外,请考虑不要在线程中运行它们,因为您可能需要进程级沙箱。例如,如果您在集成测试中遇到数据库,您不希望一个测试失败,因为另一个测试在不同的线程中添加了一些数据。大多数时候,编写测试时并没有考虑到这一点。

最后,这个问题是怎么解决的?

【讨论】:

【参考方案6】:

TestNG can do that(这是我的第一个反应——然后我看到你已经有很多测试用例了)。

对于 JUnit,请查看 parallel-junit。

【讨论】:

不幸的是,这不是我所问问题的答案。 parallel-junit 仅在单个测试类中运行。 TestNG 也只在一个类中运行,我的测试不是 TestNG 测试。 @PlatinumAzure:我更新了链接。我不知道这个项目是如何维护的。另一个问题最近被问到distribute junit tests execution on several machines。【参考方案7】:

您可以使用 Junit 本身提供的 ParallelComputer 并行运行测试。这里有一个小 sn-p 可以帮助您入门。

Class[] cls =  TestCase1.class, TestCase2.class ;
Result result = JUnitCore.runClasses(ParallelComputer.classes(), cls);
List<Failure> failures = result.getFailures();

当您需要从代码运行测试时,这会有所帮助,因为它不依赖于 Maven 或任何其他构建管理工具。

请注意,这将并行运行所有测试用例,如果您在不同测试用例之间有任何依赖关系,则可能会导致误报。无论如何,你不应该有相互依赖的测试。

【讨论】:

【参考方案8】:

另一个选择:Punner,一个新的并行 junit runner 和 maven 插件。您不必更改代码,将其复制到您的 pom.xml:

<!-- Disable default surefire based testing -->
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.20</version>
  <configuration>
    <skip>true</skip>
  </configuration>
</plugin>

<plugin>
  <groupId>com.github.marks-yag</groupId>
  <artifactId>punner-maven-plugin</artifactId>
  <version>$version</version>
  <configuration>
  </configuration>
  <executions>
    <execution>
      <id>test</id>
      <phase>test</phase>
      <goals>
        <goal>test</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Punner 可以并行运行测试方法,可以单独保持测试输出并且干净。

Punner 将减少您的 mvn 控制台输出,如下所示:

[INFO] --- punner-maven-plugin:0.9.13:test (test) @ ipc ---
[INFO] Punner report directory: /Users/guile/workspace/ipc/target/punner-reports
[INFO]
[INFO] com.github.yag.ipc.IPCTest.testConnectionHandler.............. PASSED
[INFO] com.github.yag.ipc.IPCTest.testSequence....................... PASSED
[INFO] com.github.yag.ipc.IPCTest.testPartialContent................. PASSED
[INFO] com.github.yag.ipc.IPCTest.testResponseContent................ PASSED
[INFO] com.github.yag.ipc.IPCTest.testPingPong....................... PASSED
[INFO] com.github.yag.ipc.IPCTest.testServerClose.................... PASSED
[INFO] com.github.yag.ipc.IPCTest.testServerSideHeartbeatTimeout..... PASSED
[INFO] com.github.yag.ipc.IPCTest.testClientSideHeartbeatTimeout..... PASSED
[INFO] com.github.yag.ipc.IPCTest.testClientSideHeartbeat............ PASSED
[INFO] com.github.yag.ipc.IPCTest.testClientReconnect................ PASSED
[INFO]
[INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 10.952 sec, Time saved: 25.919 sec.

Punner 产生与万能兼容的输出,您还可以从报告目录中获取原始日志数据和降价格式报告:

➜  ipc git:(develop) ll target/punner-reports
total 104
-rw-r--r--   1 guile  staff    11K Oct 15 23:07 TEST-com.github.yag.ipc.IPCTest.xml
-rw-r--r--   1 guile  staff   298B Oct 15 23:07 com.github.yag.ipc.IPCTest.txt
drwxr-xr-x  12 guile  staff   384B Oct  8 00:50 logs
-rw-r--r--   1 guile  staff    33K Oct 15 23:07 report.md

Punner 是我的个人项目,我编写 Punner 是为了加快 IPC 框架、细粒度锁定、日志服务、分布式工作流引擎等其他项目的单元测试阶段。它节省了我很多等待时间。

Punner 还不支持某些高级功能。如果你能尝试一下并给我一些反馈,我很高兴。

【讨论】:

我在查找给定插件或网站的源代码时遇到问题。请添加项目主页的链接。【参考方案9】:

您可以在一分钟内将您的测试更改为 TestNg 测试(您只需更改导入),TestNG 是并行测试中最好的。

【讨论】:

【参考方案10】:

您可以尝试Gridgain,它可以让您在计算网格中运行分布测试。

【讨论】:

我已经尝试了 GridGain 解决方案并且遇到了两个主要问题。首先,您必须告诉 GridGain 从您的网格任务的类路径中排除 GridGain 也使用的任何内容,例如Spring 和许多 Apache Commons 的东西。其次,网络类加载虽然是一个绝妙的主意,但不适用于想要搜索类路径的库,例如春天

以上是关于在 Maven 构建中并行运行 junit 测试?的主要内容,如果未能解决你的问题,请参考以下文章

并行 JUnit 测试不执行关闭挂钩

jenkins maven 构建未运行 JUnit 测试

如何使用 Maven surefire 排除具有给定类别的所有 JUnit4 测试?

如何同时使用 maven 和 jUnit 测试?

使用 gradle 运行并行测试任务

在Maven项目中运行JUnit 5测试用例