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)。
练习题:
- 为你之前写的
Guessing Game添加单元测试,验证输入验证逻辑。 - 在
src/lib.rs中编写文档注释(Libraries doc comments),包含一个代码示例,并运行cargo test验证文档测试是否通过。
思考题:
TDD (测试驱动开发) 提倡先写测试后写代码。在 Rust 这种强类型且编译检查严格的语言中,TDD 的体验和动态语言(如 Ruby/Python/JS)有什么不同?
本文代码示例:
关注公众号:极客老墨
更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。
