完整示例
被测试的代码
rust
▶ Run// src/lib.rs
/// 计算斐波那契数列
pub fn fibonacci(n: u32) -> u64 {
match n {
0 => 0,
1 => 1,
_ => {
let mut a = 0;
let mut b = 1;
for _ in 2..=n {
let temp = a + b;
a = b;
b = temp;
}
b
}
}
}
/// 计算最大值
pub fn max<T: Ord>(a: T, b: T) -> T {
if a > b { a } else { b }
}
/// 用户结构体
pub struct User {
pub name: String,
pub age: u32,
}
impl User {
pub fn new(name: String, age: u32) -> Self {
User { name, age }
}
pub fn name(&self) -> &str {
&self.name
}
pub fn is_adult(&self) -> bool {
self.age >= 18
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fibonacci() {
assert_eq!(fibonacci(0), 0);
assert_eq!(fibonacci(1), 1);
assert_eq!(fibonacci(5), 5);
assert_eq!(fibonacci(10), 55);
}
#[test]
fn test_max() {
assert_eq!(max(1, 2), 2);
assert_eq!(max("a", "b"), "b");
}
#[test]
fn test_user_is_adult() {
let user = User::new(String::from("Alice"), 20);
assert!(user.is_adult());
let minor = User::new(String::from("Bob"), 15);
assert!(!minor.is_adult());
}
}集成测试
rust
▶ Run// tests/integration_test.rs
use my_crate::{User, fibonacci};
#[test]
fn test_fibonacci_sequence() {
let expected = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34];
for (i, &exp) in expected.iter().enumerate() {
assert_eq!(fibonacci(i as u32), exp);
}
}
#[test]
fn test_user_creation() {
let user = User::new(String::from("Charlie"), 30);
assert_eq!(user.name(), "Charlie");
assert!(user.is_adult());
}测试最佳实践
测试命名
rust
▶ Run#[test]
fn test_add_positive_numbers() {}
#[test]
fn test_add_negative_numbers() {}
#[test]
fn test_add_mixed_numbers() {}
// 或使用描述性名称(推荐)
#[test]
fn add_returns_sum_of_two_numbers() {}
#[test]
fn add_zero_to_number_returns_number() {}
#[test]
fn add_negative_numbers_returns_correct_sum() {}AAA 模式
rust
▶ Run#[test]
fn test_user_discount() {
// Arrange(准备)
let user = User::new(String::from("Alice"), 25);
let cart = Cart::new();
cart.add_item(Item::new("Book", 20.0));
// Act(执行)
let total = cart.calculate_total(&user);
// Assert(断言)
assert_eq!(total, 18.0); // 10% 折扣
}测试夹具
rust
▶ Run#[cfg(test)]
mod tests {
use super::*;
// 测试夹具
struct TestFixture {
user: User,
cart: Cart,
}
impl TestFixture {
fn new() -> Self {
TestFixture {
user: User::new(String::from("Test"), 25),
cart: Cart::new(),
}
}
}
impl Drop for TestFixture {
fn drop(&mut self) {
// 清理资源
}
}
#[test]
fn test_checkout() {
let mut fixture = TestFixture::new();
fixture.cart.add_item(Item::new("Book", 20.0));
let total = fixture.cart.calculate_total(&fixture.user);
assert_eq!(total, 18.0);
}
}常见错误
错误 1:忘记 #[cfg(test)]
rust
▶ Run// ❌ 错误:测试代码会被编译到生产代码
mod tests {
// ...
}
// ✅ 正确
#[cfg(test)]
mod tests {
// ...
}错误 2:测试依赖外部状态
rust
▶ Run// ❌ 不好
#[test]
fn test_with_global_state() {
// 依赖全局变量或时间
}
// ✅ 好
#[test]
fn test_isolated() {
// 自包含的测试,不依赖外部状态
}错误 3:测试过于复杂
rust
▶ Run// ❌ 不好
#[test]
fn test_everything() {
// 100 行测试代码,测试多个功能
}
// ✅ 好
#[test]
fn test_single_feature() {
// 只测试一个功能
}练习
练习 1:单元测试
为你的项目编写单元测试,覆盖所有公共函数。
练习 2:集成测试
创建集成测试验证模块间交互。
练习 3:文档测试
为公共 API 编写文档测试。
小结
本章我们学习了:
- ✅ 单元测试
- ✅ 集成测试
- ✅ 文档测试
- ✅ 断言宏
- ✅ 基准测试
- ✅ 生成文档
- ✅ 测试覆盖率
测试命令速查
| 命令 | 说明 |
|---|---|
cargo test | 运行所有测试 |
cargo test test_name | 运行特定测试 |
cargo test -- --nocapture | 显示输出 |
cargo test --doc | 只运行文档测试 |
cargo doc --open | 生成并打开文档 |
cargo llvm-cov | 生成覆盖率报告 |
cargo bench | 运行基准测试 |
测试金字塔
/\
/ \
/ E2E\ 端到端测试(少量)
/______\
/ \
/Integration\ 集成测试(适量)
/______________\
/ Unit Tests \ 单元测试(大量)
/____________________\测试建议
- 多写单元测试 - 快速、隔离、易维护
- 适量集成测试 - 验证模块间交互
- 文档测试 - 保证示例代码正确
- 避免过度测试 - 测试实现细节会降低重构能力
教程完成!祝你 Rust 学习愉快!