Rust 学习笔记 17:自动化测试 (Automated Testing)

“Program testing can be a very effective way to show the presence of bugs, but it is hopelessly inadequate for showing their absence.” – Edsger W. Dijkstra

在 Go 中,我们创建 _test.go 文件,写 func TestXxx(t *testing.T)。 在 Rust 中,测试是一等公民。你不需要特殊的目录结构(单元测试),也不需要额外的测试框架。

1. 单元测试 (Unit Tests)

按照惯例,Rust 的单元测试直接写在源代码文件中,通常放在底部的 tests 模块里。

 1// src/lib.rs
 2
 3fn add(a: i32, b: i32) -> i32 {
 4    a + b
 5}
 6
 7#[cfg(test)]
 8mod tests {
 9    use super::*; // 引入父模块的所有内容
10
11    #[test]
12    fn it_works() {
13        assert_eq!(add(2, 2), 4);
14    }
15}
  • #[cfg(test)]:告诉编译器,只有在运行 cargo test 时才编译这个模块。生成的二进制文件不会包含这些测试代码,零空间开销
  • #[test]:标记一个函数为测试函数。
  • assert_eq! / assert!:断言宏。

2. 测试私有函数

Rust 的单元测试允许测试私有函数!这是很多语言做不到的(Java 需要反射或放宽可见性)。 因为测试模块就定义在源码文件内部,且是子模块,它天然拥有访问父模块私有内容的权限。

3. 恐慌测试与 Result 测试

有时候我们需要验证"代码是否按预期报错"。

1#[test]
2#[should_panic(expected = "Guess value must be between 1 and 100")]
3fn greater_than_100() {
4    Guess::new(200);
5}

测试函数也可以返回 Result<(), String>,这样你就可以在测试中使用 ? 运算符了,非常方便。

4. 集成测试 (Integration Tests)

单元测试关注函数级逻辑,集成测试关注库的公开接口。 集成测试放在项目根目录下的 tests 目录(与 src 同级)。

1my_project/
2├── Cargo.toml
3├── src/
4│   └── lib.rs
5└── tests/
6    └── integration_test.rs

tests/integration_test.rs:

1use ch17_testing;
2
3#[test]
4fn it_adds_two() {
5    assert_eq!(ch17_testing::add(2, 2), 4);
6}

Cargo 会把 tests 目录下的每个文件都编译成一个独立的 Crate。

5. 小结

第十七篇笔记。

  • Cargo test 是一切的入口。
  • 单元测试 就在源码里,用 #[cfg(test)] 隔离。
  • 集成测试tests/ 目录,模拟外部用户。

Rust 的测试文化非常浓厚,文档测试 (Doc Tests) 更是 Rust 的一大杀器(我们在代码注释里写的 Example 代码会被自动运行测试!)。

下一篇,我们将探讨 Rust 函数式编程的左膀右臂:闭包 (Closures) 和 迭代器 (Iterators)


练习题

  1. 为你之前写的 Guessing Game 添加单元测试,验证输入验证逻辑。
  2. src/lib.rs 中编写文档注释(Libraries doc comments),包含一个代码示例,并运行 cargo test 验证文档测试是否通过。

思考题

TDD (测试驱动开发) 提倡先写测试后写代码。在 Rust 这种强类型且编译检查严格的语言中,TDD 的体验和动态语言(如 Ruby/Python/JS)有什么不同?


本文代码示例

关注公众号:极客老墨

更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。

极客老墨微信公众号二维码

相关阅读