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

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

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

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

有什么办法吗?

【问题讨论】:

    标签: java maven junit


    【解决方案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 的东西。其次,网络类加载虽然是一个绝妙的主意,但不适用于想要搜索类路径的库,例如春天
            猜你喜欢
            • 2016-03-09
            • 2018-06-18
            • 2013-08-23
            • 1970-01-01
            • 2013-11-08
            • 2019-12-17
            • 2011-11-12
            • 2021-12-16
            相关资源
            最近更新 更多