在 PlanetScale,我们的后端 API 是基于 Ruby on Rails 构建的。它是一款标准的 Rails 应用程序,我们使用 minitest 作为测试套件,并通过 FactoryBot 来生成测试数据。

我们团队的每个人都曾参与过具有缓慢测试套件的 Rails 应用的开发,这种慢速测试对团队生产力的影响令人头痛。随着我们的应用规模不断增长,我们持续投入时间优化测试套件的速度。我们明白快速的反馈循环对团队效率的巨大价值,而在测试套件上做出一点额外的努力能够让我们开发的新功能更加轻松。

本地开发

在本地开发环境中,我们从不运行整个应用程序的测试套件。这种做法效率低下,本地运行测试套件永远无法与 CI(持续集成)上的速度相媲美。在本地开发时,我们通常只运行修改过的文件对应的测试,或者仅运行一个单独的测试。从本地提交代码后,我们快速获取关于整个测试套件的反馈。

在 MacBook Pro 上运行完整的测试套件,大约需要 12 分钟,并且是串行运行。我们对本地测试速度没有投入太多精力优化,因为工程师们实际上并不会在本地运行所有测试。

在 CI 上运行并行测试

Rails 现在支持通过 minitest 并行运行测试。如果使用其他的测试框架,还有各种 gem 可以实现类似功能。

并行测试的引入对测试套件速度带来了最大影响,并且是最快捷的优化手段。起初,我们在 CI 上通过两个 worker 并行运行测试。并行的效率取决于运行机器的核心数。

这种方法虽然带来了一些速度提升,但我们希望实现更快的测试反馈。我们的基础设施团队为我们配置了带 64 个核心的机器在 Buildkite 上运行。

Ruby1# 仅在 CI 上并行运行
2if ENV["CI"]
3  parallelize(workers: 64)
4end

在应用上述更改后,我们的测试套件运行时间降至大约 3-4 分钟。不过,我们发现还有一些问题需要解决。接下来,我们开始优化具体的测试逻辑。

审查 FactoryBot

经过一番调查,我们发现大部分测试时间都消耗在创建测试数据上。我们使用 FactoryBot 来管理数据。

为了更好地理解问题,我们在测试中使用调试工具,在测试设置完成后暂停代码执行。这里我们使用了 pry,以便查看所有创建的对象并验证它们是否符合预期。令人惊讶的是,我们在一些地方创建的对象数量竟然是预计的 8 倍。

这是使用 FactoryBot 时的一个常见错误。该库让设定数据之间的关系变得非常简单,但有时会意外地触发关联对象的过度创建。

修复我们的 Factory

找出问题后,解决起来就相对简单了。我们为 Factory 的期望设置了测试,确保它不会意外地创建超过预期的对象。

Ruby1test "factory doesn’t create tons of databases" do
2  create(:database)
3  assert_equal 1, Database.count
4end

起初这些测试是失败的,但通过逐步调整测试模板,最终让它只创建正确数量的对象。

这些改进进一步提升了效率,测试运行时间降至大约 1 分钟。

为了防止未来在修改 Factory 时出现回归问题,我们将这些测试保存在模型中,以便随时验证 Factory 的行为。


优化测试套件速度不仅能提高开发效率,还能减轻团队开发过程中的反馈负担。通过并行测试和精简 FactoryBot 的使用,我们的 Rails 测试套件成功从 12 分钟降至 1 分钟,为开发流程带来了更快、更高效的实践基础。



如何让我们的 Rails 测试套件在 Buildkite 上运行仅需 1 分钟插图

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:http://choupangxia.cn/2025/05/20/rails-buildkite/