别只会 cargo test:用 cargo-nextest 把 Rust CI 测试变成可治理的工程系统
Rust 项目刚起步时,cargo test 往往足够直接:执行测试、看失败输出、修完再跑一遍。但当仓库演变为 workspace,测试中加入数据库、网络或外部服务后,CI 的问题会变得不只是“有没有红灯”:偶发失败需要重跑,资源竞争让集成测试互相干扰,慢测试拖长反馈周期,而测试结果也难以进入现有的报告系统。
cargo-nextest 是 Rust 的下一代测试运行器。它以 Cargo 子命令的形式工作,核心目标不是替你写测试,而是让测试执行拥有可配置的重试、超时、分组与机器可读报告能力。对于小型 crate,它未必是必需依赖;但对 workspace 和持续集成来说,它很适合作为把“跑测试”升级为“治理测试”的低迁移成本工具。
从一条替换命令开始
官方从源码安装的方式如下;--locked 不是装饰项,官方文档明确说明普通的 cargo install cargo-nextest 不受支持。
cargo install cargo-nextest --locked
安装完成后,在 Rust workspace 根目录运行:
cargo nextest run
它不是独立的 nextest 二进制命令,而是注册为 Cargo 子命令,因此命令前缀始终是 cargo nextest。常见的包级筛选也沿用 Cargo 的习惯:
cargo nextest run -p my-package
这一步的价值是把迁移风险降到很低:先让 CI 的测试阶段从 cargo test 切换到 cargo nextest run,观察执行时间与失败输出;确认无差异后,再针对真实痛点加入配置。不要一开始就把所有测试策略“调到最复杂”。
把偶发失败从口头经验变成仓库规则
nextest 的仓库级配置放在 workspace 根目录的 .config/nextest.toml,可以随代码进入版本控制。也就是说,重试、超时和分组规则不再藏在某个 CI 平台的临时脚本里,而能和测试本身一起被审查。
下面是一份适合 CI 起步的配置。它为 ci profile 设置固定间隔的两次重试,并把 JUnit 报告写到指定路径:
[profile.ci]
retries = { backoff = "fixed", count = 2, delay = "1s" }
[profile.ci.junit]
path = "junit.xml"
然后在 CI 中运行:
cargo nextest run --profile ci
这里的“重试”不应该成为掩盖问题的按钮。它适合处理已知的短暂网络波动、启动时序或外部依赖抖动;如果某个纯单元测试反复需要重试,更合理的动作通常是修正共享状态、时间依赖或测试隔离。nextest 还支持用 --flaky-result fail 让“重跑后通过”的测试仍使任务失败,这很适合团队把 flaky test 作为需要跟踪的质量信号,而不是悄悄放过它。
给会竞争资源的测试一条单独跑道
集成测试经常共享端口、临时目录、测试数据库或有限的第三方配额。盲目降低整个测试套件的并发度会让 CI 变慢;更好的办法是只限制那一小组会冲突的测试。
nextest 的 test group 可以给命名组设置并发上限。官方示例中的 serial-integration 使用 max-threads = 1,其含义是被分到这一组的测试一次只运行一个:
[test-groups]
serial-integration = { max-threads = 1 }
接下来应当通过 per-test override 把真正依赖同一外部资源的测试放进该组,而不是把整个 workspace 都串行化。这样,普通单元测试继续并行,数据库迁移、固定端口或共享 sandbox 的测试则获得明确的资源边界。配置完成后,可用 cargo nextest show-config test-groups 查看当前生效的分组,避免“我以为配置加载了”的 CI 误判。
不只看总分:让 CI 认识测试结果
很多 CI 系统能展示 JUnit/XUnit XML 报告。nextest 的 JUnit 支持采用 Jenkins XML 格式;当上面的 ci profile 生效时,报告会写到 workspace 中的 target/nextest/ci/junit.xml。这使平台能按测试用例呈现失败、耗时与重试信息,而不必让开发者在一长串终端日志中手工定位。
要注意,报告格式解决的是可见性,不会自动解决测试设计问题。建议把下面三类数据一起看:
- 总耗时持续上涨的测试,优先检查不必要的 I/O、重复构建和可并行的准备步骤;
- 经常重试才通过的测试,记录其外部依赖与失败模式,设定修复期限;
- 被放入串行组的测试,定期审视是否还能拆分资源,避免串行组逐渐膨胀。
什么时候值得采用?
如果项目只有少量纯函数单元测试,cargo test 简洁且足够好。反过来,出现以下任一信号时,nextest 值得评估:Rust workspace 的 CI 已经很慢;测试会抢端口、数据库或共享环境;团队需要结构化报告;或者偶发失败已经让人习惯性地点“重新运行”。
最务实的采用顺序是:先在一个 CI job 使用 cargo nextest run 验证兼容性,再提交最小 .config/nextest.toml,最后只为有证据的资源冲突和 flaky test 增加规则。测试基础设施的成熟,不是堆更多开关,而是让失败、并发和质量风险都能被看见、复现并持续改善。
相关链接