测试
你会学到什么
- 用
#[test]标注测试函数,cargo test自动运行。 - 单元测试放在被测模块内,可访问私有项;集成测试放在
tests/目录,只用公开 API。 - 用
assert!、assert_eq!和#[should_panic]表达期望。
最小示例
#[cfg(test)]
mod tests {
#[test]
fn adds() {
assert_eq!(2 + 3, 5);
}
}
运行代码
cd examples
cargo run -p rt_30_testing
cargo test -p rt_30_testing
代码讲解
单元测试惯例上放在同一文件的 #[cfg(test)] mod tests 里,#[cfg(test)] 保证测试代码不会进入正式构建:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detects_even() {
assert!(is_even(4));
}
}
集成测试放在 crate 根的 tests/ 目录,每个文件是一个独立 crate,只能访问 pub API——这正好验证你的公开接口好不好用:
// tests/integration.rs
use rt_30_testing::add;
#[test]
fn adds_across_crate_boundary() {
assert_eq!(add(10, 20), 30);
}
#[should_panic(expected = "...")] 断言某段代码会 panic 且信息匹配。
常见错误
忘记 use super::*;,测试里访问不到被测函数:
#[cfg(test)]
mod tests {
#[test]
fn adds() { assert_eq!(add(2, 3), 5); } // ❌ add 不在作用域
}
在 mod tests 顶部加 use super::*;。
练习
- 给
add写一个测试覆盖负数相加。 - 在
tests/里新增一个文件,测试is_even的边界值。
小结
cargo test 一条命令跑遍单元测试、集成测试和文档测试。单元测试查内部,集成测试查公开 API。
下一步
好的库还要有好文档。下一章学习文档注释与文档测试。
完整示例代码
下面是 examples/30_testing/ 的完整源码。无需 clone 仓库,直接在页面上阅读、复制、对照运行。
examples/30_testing/src/main.rs
use rt_30_testing::{add, is_even};
fn main() {
println!("2 + 3 = {}", add(2, 3));
println!("is_even(4) = {}", is_even(4));
} examples/30_testing/src/lib.rs
//! 演示单元测试与集成测试。
/// 两数相加。
pub fn add(left: i64, right: i64) -> i64 {
left + right
}
/// 判断是否为偶数。
pub fn is_even(value: i64) -> bool {
value % 2 == 0
}
// 单元测试:和被测代码放在同一文件,可访问私有项。
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn adds() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn detects_even() {
assert!(is_even(4));
assert!(!is_even(5));
}
#[test]
#[should_panic(expected = "boom")]
fn can_assert_panic() {
panic!("boom");
}
} examples/30_testing/tests/integration.rs
//! 集成测试:像外部用户一样,只能访问公开 API。
use rt_30_testing::{add, is_even};
#[test]
fn adds_across_crate_boundary() {
assert_eq!(add(10, 20), 30);
}
#[test]
fn checks_even_across_crate_boundary() {
assert!(is_even(0));
assert!(!is_even(-3));
} examples/30_testing/Cargo.toml
[package]
name = "rt_30_testing"
version.workspace = true
edition.workspace = true
publish.workspace = true