为什么 println! 在 Rust 测试中保持沉默?全面解析

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:“沉默”的 println! — 每个 Rust 新手都熟悉的痛点

\n\n

您正在编写您的第一个 Rust 单元测试。一切都很合理:创建一个结构体,实现一个方法,然后——激动人心的验证时刻。您在测试中插入 println!,以确保数据正确。运行 cargo test... 您只看到一行干巴巴的文字:“test result: ok. 1 passed; 0 failed”。没有“Hello, world!”,没有中间值。您的 println! 就这样消失了。

\n\n

熟悉吗?欢迎加入这个俱乐部。这个问题在 Stack Overflow 上获得了数千次浏览,它是所有学习 Rust 测试 的人的真正绊脚石。在这篇文章中,我们不仅仅会说“使用 --nocapture”。我们将深入探讨 为什么 会发生这种情况,测试运行器中的 输出捕获 是如何工作的,以及如何在不违反单元测试原则的情况下正确调试代码。

\n\n

为什么 Rust 在测试中“屏蔽”了 println!?

\n\n

这不是一个 bug,也不是语言创造者的恶意。这是一个有意的架构决策。当您运行 cargo test 时,Rust 使测试变得干净且可预测。想象一下,您有 500 个测试,每个测试都向 stdout 输出 10 行调试信息。您的终端将变成一团乱麻,最重要的是,您将无法区分真正的错误和调试垃圾信息。

\n\n

默认情况下,测试运行器(test 库)会拦截所有 标准输出 (std::io::stdout()) 和 标准错误输出 (std::io::stderr())。它在系统描述符级别执行此操作。println! 正是写入 stdout,因此其输出会进入运行器的“黑匣子”。输出仅在测试 失败 时显示——以便您可以看到哪里出了问题。

\n\n

在代码中是什么样子?

\n\n

让我们看一个重现该问题的简单示例:

\n\n
// src/lib.rs\npub fn add_two(a: i32) -> i32 {\n    a + 2\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_add_two() {\n        println!("我在执行,但你看不到我!");\n        let result = add_two(2);\n        println!("结果: {}", result);\n        assert_eq!(result, 4);\n    }\n}
\n\n

运行:

\n\n
$ cargo test\n   Compiling my_project v0.1.0\n    Finished test [unoptimized + debuginfo] target(s) in 0.5s\n     Running unittests src/lib.rs (target/debug/deps/my_project-abc123)\n\nrunning 1 test\ntest tests::test_add_two ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
\n\n

如您所见,println! 的消息都没有出现。测试通过了,运行器认为没有什么需要向您展示的。

\n\n

如何“唤醒” println!?方法及注意事项

\n\n

主要有三种方法,每种方法都适用于不同的情况。

\n\n

方法 1:--nocapture 标志(最简单且最流行)

\n\n

这就是您在 Stack Overflow 的第一个答案中找到的内容。--nocapture 标志会禁用当前运行中 所有 测试的输出捕获。

\n\n
$ cargo test -- --nocapture
\n\n

请注意双破折号 --。它将 cargo 的参数与测试二进制文件本身的参数分开。-- 之后的所有内容都会传递给测试运行器。

\n\n

现在输出将如下所示:

\n\n
$ cargo test -- --nocapture\n   Compiling my_project v0.1.0\n    Finished test [unoptimized + debuginfo] target(s) in 0.5s\n     Running unittests src/lib.rs (target/debug/deps/my_project-abc123)\n\nrunning 1 test\n我在执行,但你看不到我!\n结果: 4\ntest tests::test_add_two ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered                

博客

书籍推荐