Rust 测试¶
此文件使用 Rust 特定内容扩展 common/testing.md。
测试框架¶
- 单元测试使用
#[cfg(test)]模块中的#[test] - 参数化测试和 fixture 使用 rstest
- 基于属性的测试使用 proptest
- 基于 trait 的 mock 使用 mockall
- 异步测试使用
#[tokio::test]
测试结构¶
my_crate/
├── src/
│ ├── lib.rs # #[cfg(test)] モジュール内のユニットテスト
│ ├── auth/
│ │ └── mod.rs # #[cfg(test)] mod tests { ... }
│ └── orders/
│ └── service.rs # #[cfg(test)] mod tests { ... }
├── tests/ # 統合テスト(各ファイル = 個別のバイナリ)
│ ├── api_test.rs
│ ├── db_test.rs
│ └── common/ # 共有テストユーティリティ
│ └── mod.rs
└── benches/ # Criterion ベンチマーク
└── benchmark.rs
单元测试放在同一文件中的 #[cfg(test)] 模块里。集成测试放在 tests/ 中。
单元测试模式¶
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn creates_user_with_valid_email() {
let user = User::new("Alice", "alice@example.com").unwrap();
assert_eq!(user.name, "Alice");
}
#[test]
fn rejects_invalid_email() {
let result = User::new("Bob", "not-an-email");
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("invalid email"));
}
}
参数化测试¶
use rstest::rstest;
#[rstest]
#[case("hello", 5)]
#[case("", 0)]
#[case("rust", 4)]
fn test_string_length(#[case] input: &str, #[case] expected: usize) {
assert_eq!(input.len(), expected);
}
异步测试¶
#[tokio::test]
async fn fetches_data_successfully() {
let client = TestClient::new().await;
let result = client.get("/data").await;
assert!(result.is_ok());
}
使用 mockall 进行模拟¶
在生产代码中定义 trait,并在测试模块中生成 mock:
// 本番トレイト — 統合テストがインポートできるように pub にする
pub trait UserRepository {
fn find_by_id(&self, id: u64) -> Option<User>;
}
#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::eq;
mockall::mock! {
pub Repo {}
impl UserRepository for Repo {
fn find_by_id(&self, id: u64) -> Option<User>;
}
}
#[test]
fn service_returns_user_when_found() {
let mut mock = MockRepo::new();
mock.expect_find_by_id()
.with(eq(42))
.times(1)
.returning(|_| Some(User { id: 42, name: "Alice".into() }));
let service = UserService::new(Box::new(mock));
let user = service.get_user(42).unwrap();
assert_eq!(user.name, "Alice");
}
}
测试命名¶
使用描述性的名称来说明场景:
- creates_user_with_valid_email()
- rejects_order_when_insufficient_stock()
- returns_none_when_not_found()
覆盖率¶
- 目标行覆盖率达到 80% 以上
- 覆盖率报告使用 cargo-llvm-cov
- 关注业务逻辑 — 排除生成代码和 FFI 绑定
cargo llvm-cov # サマリー
cargo llvm-cov --html # HTML レポート
cargo llvm-cov --fail-under-lines 80 # しきい値以下で失敗
测试命令¶
cargo test # すべてのテストを実行
cargo test -- --nocapture # println 出力を表示
cargo test test_name # パターンに一致するテストを実行
cargo test --lib # ユニットテストのみ
cargo test --test api_test # 特定の統合テスト(tests/api_test.rs)
cargo test --doc # ドキュメントテストのみ
参考¶
有关包含基于属性的测试、fixture 和使用 Criterion 进行基准测试的全面测试模式,请参见技能:rust-testing。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC