如何通过并行 CI 机器节省运行自动化测试的时间

作者:Artur Trzop
Knapsack Pro Ruby JavaScript Tests

自动化测试是许多编程项目的一部分,确保软件完美无瑕。项目越大,测试套件就越大。这可能导致自动化测试花费大量时间运行。在本文中,您将学习如何通过并行持续集成(CI)机器更快地运行自动化测试,以及可能遇到的问题。本文涵盖了常见的并行测试问题,基于 Ruby 和 JavaScript 测试。

Knapsack Pro Logo慢速自动化测试

当程序员停止在其本地机器上运行整个测试套件时,自动化测试可以被认为是慢速的,因为它太耗时了。大多数时候,您使用 CI 服务器,如 Jenkins、CircleCI、Github Actions,在外部机器而不是您自己的机器上运行测试。当您的测试套件运行一个小时时,在您的计算机上运行它效率不高。您的 Web 项目的浏览器端到端测试可能需要很长时间才能执行。在 CI 服务器上运行测试一个小时效率也不高。作为开发人员,您需要快速的反馈循环,以了解您的软件是否运行良好。自动化测试应该帮助您实现这一点。

在多个 CI 机器之间拆分测试以节省时间

节省时间的一种方法是使 CI 构建尽可能快。当您的测试需要 1 小时才能运行时,您可以利用您的 CI 服务器配置并设置并行作业(并行 CI 机器/节点)。每个并行作业可以运行一部分测试套件。

您需要将测试在并行 CI 机器之间进行划分。当您有一个 60 分钟的测试套件时,您可以运行 20 个并行作业,其中每个作业运行一小部分测试,这应该可以节省您的时间。在最佳情况下,您每个作业将运行 3 分钟的测试。

如何确保每个作业运行 3 分钟?作为第一步,您可以应用一个简单的解决方案。按字母顺序对所有测试文件进行排序,并按并行作业的数量进行划分。您的每个测试文件可能具有不同的执行时间,具体取决于每个测试文件中的测试用例数量以及每个测试用例的复杂程度。但是您最终可能会以次优的方式划分测试文件,这会带来问题。下图说明了并行 CI 作业之间测试的次优拆分,其中一个作业运行的测试过多,最终成为瓶颈。

Knapsack Pro Suboptimal Distribution of Tests
次优的测试分配 - 红色条表示节点花费的时间过长

基于测试时间的静态测试拆分

您可以使用每个测试文件的执行时间来预测如何在并行作业之间划分测试套件,以确保每个作业花费的时间相似。

您可以使用开源工具,如 knapsack ruby gem,专为 Ruby 及其测试运行器(如 RSpec、Minitest、Cucumber)设计。您甚至可以使用 knapsack_pro gem 跟踪您的所有提交并记录每个 CI 构建的测试执行时间,以便更好地预测您的下一个 CI 构建应如何在并行 CI 作业之间分配测试。

基于测试文件执行时间在并行作业之间划分测试文件存在一些限制。例如,当您的测试文件具有不确定的执行时间时。当您的端到端浏览器测试需要加载复杂的网页,并且浏览器向有时响应缓慢的外部 API 发出许多请求时,可能会发生这种情况。完全加载页面需要许多组件,这会影响测试用例的速度。有时网页加载速度更快或更慢。这会影响运行测试文件所需的时间。通常会产生复合效应,您的并行作业的运行时间可能会比预期的 3 分钟长得多,这可能会导致瓶颈作业运行时间过长,从而减慢您的 CI 构建速度。

另一个常见问题是每个并行作业的启动时间。通常,在开始运行测试之前,您需要准备环境,为此,您需要在并行作业内部安装依赖项,或从缓存加载数据。这可能导致所有并行作业在不同的时间点开始工作。因此,最后启动的并行作业可能会成为整个 CI 构建的瓶颈。

并行作业之间的动态测试拆分

为了解决静态测试拆分的问题,您可以使用动态测试拆分。这个想法很简单。您有一个测试文件队列。然后,每个并行作业从队列中获取一些测试文件并执行它们。完成后,它从队列中获取另一组测试文件。如此继续,直到整个队列被消耗完。这样,如果其中一个并行作业碰巧运行慢速测试用例,那么它最终只会从队列中消耗较少的测试文件。对于由于环境设置缓慢而延迟开始工作的作业也是如此。它也将运行较少的测试用例。

下图显示了动态拆分如何与队列一起工作。

Knapsack Pro API
并行作业从队列中消耗测试文件

动态测试拆分应在并行 CI 作业之间产生最佳的测试套件拆分,如下面的图表所示。

Knapsack Pro Optimal Test Suite
并行作业之间的最佳测试套件拆分

如何使用队列实现动态测试拆分

如果您是使用 Jest 或 Cypress 进行测试的 Ruby 或 JavaScript 开发人员,则可以使用 Knapsack Pro 工具在您的 CI 服务器上拆分您的测试文件。它是一个 CI 不可知的解决方案,可与任何 CI 提供商配合使用。下面您可以看到一个示例 YAML 配置,它类似于您当前的 CI 服务器配置。

jobs:

 - name: Run Ruby tests with Knapsack Pro
   parallelism: 10 # run 10 parallel CI nodes
   commands:
     # Run RSpec specs in parallel
     - run: bundle exec knapsack_pro:queue:rspec
    
     # Run Minitest tests in parallel
     - run: bundle exec knapsack_pro:queue:minitest
    
     # Run Cucumber tests in parallel
     - run: bundle exec knapsack_pro:queue:cucumber
    
     # ... other Ruby test runners here


 - name: Run JS tests with Knapsack Pro
   parallelism: 4
   commands:
     # Run Cypress tests in parallel
     - $(npm bin)/knapsack-pro-cypress
    
     # Run Jest tests in parallel
     - $(npm bin)/knapsack-pro-jest

[源代码可以在 https://gist.github.com/ArturT/580df4fd7852e67379e9b263228e1994#file-ci-server-config-yaml 找到 ]

Ruby 开发人员还可以找到专用于 RSpec 测试运行器的有用功能,该功能可自动检测慢速测试文件并根据单个测试用例对其进行划分。这样,您的 慢速 RSpec 测试文件也可以在并行作业中运行

我希望您觉得这很有用,并且您可以通过利用快速并行 CI 构建来节省日常工作时间。

Artur Trzop,自 2012 年起专业从事 Ruby 工作的软件工程师。开源 knapsack ruby​​ gem 的作者和 KnapsackPro.com 的创始人 - 用于在 CI 环境中更快地运行自动化测试的解决方案。

加载 Disqus 评论