cargo test 默认并行导致输出乱序是因为多线程独立写入 stdout/stderr;--test-threads=1 可保证输出有序且避免状态冲突,但牺牲执行速度,仅应针对有全局状态依赖的测试使用。

为什么 cargo test 默认并行但输出乱序?
Rust 的测试运行器默认启用多线程(--test-threads=auto),目的是加速执行。但每个测试线程会独立写 stdout/stderr,导致 println! 或日志混在一起,比如你看到的不是“test_a → test_b → test_c”,而是“tes”“t_a\nok”“st_b\nok”这种碎片化输出。这不是 bug,是并发 I/O 的自然表现。
--test-threads=1 能解决乱序,但代价是什么?
设为 1 确实让所有测试串行执行、输出严格有序,也彻底规避共享状态冲突(如临时文件名重复、环境变量覆盖)。但注意两点:
- 它不改变测试函数本身的执行逻辑——只是强制单线程调度,所有
#[test]还是各自独立运行 - 性能损失显著:100 个耗时 100ms 的测试,在 4 核机器上并行约需 250ms;串行则要 10s
- 仅对当前命令生效,不影响
cargo test --lib或cargo test --bin xxx的其他调用
什么时候不该用 --test-threads=1?
多数纯计算型测试(无 IO、无全局状态)完全没必要禁用并行。真正需要串行的场景很具体:
- 测试里调用了
std::env::set_var并依赖后续测试读取该变量 - 多个测试共用同一个磁盘路径(如
"./tmp"),且没加随机后缀或tempfilecrate - 测试中启动了本地 HTTP 服务,端口固定且未做端口探测/释放
- 使用了
static mut或裸指针修改全局可变状态(极不推荐,但 legacy code 可能存在)
如果只是想看输出,优先用 --nocapture + 保留并行,而不是直接降为 1 线程。
更精细的控制:只对特定测试禁用并行
没必要一刀切。你可以:
- 给有状态的测试加
#[cfg(test)]模块隔离,并在该模块内用std::sync::Mutex或tempfile::TempDir管理资源 - 单独运行问题测试:
cargo test my_stateful_test -- --test-threads=1,其它仍并行 - 在 CI 中区分:开发时用
--test-threads=1快速定位顺序问题;CI pipeline 用默认并行保速度
真正难处理的不是线程数本身,而是测试间隐式耦合——修复根源比调参数更重要。











