Rust TDD 命令¶
此命令强制使用 #[test]、rstest、proptest、mockall 的 Rust 代码测试驱动开发方法。
此命令的功能¶
- 类型/特征定义:使用
todo!()搭建函数签名 - 创建测试:创建全面的测试模块(RED)
- 运行测试:确认测试因正确原因失败
- 实现代码:编写使测试通过的最小代码(GREEN)
- 重构:在保持测试绿色的同时改进代码
- 检查覆盖率:使用 cargo-llvm-cov 确保 80% 以上的覆盖率
使用时机¶
在以下情况下请使用 /rust-test:
- 实现新的 Rust 函数、方法或特征时
- 为现有 Rust 代码添加测试覆盖率时
- 修复 bug 时(先编写失败的测试)
- 构建关键业务逻辑时
- 学习 Rust 中的 TDD 工作流时
TDD 循环¶
RED -> 先に失敗するテストを書く
GREEN -> テストを通過する最小限のコードを実装する
REFACTOR -> コードを改善し、テストはグリーンを維持する
REPEAT -> 次のテストケースへ
会话示例¶
User: /rust-test ユーザー登録を検証する関数が必要です
エージェント:
# TDD セッション: 登録バリデーター
## ステップ 1: インターフェースの定義
```rust
// src/validator.rs
pub struct RegistrationRequest {
pub name: String,
pub email: String,
pub password: String,
}
pub enum ValidationResult {
Valid,
Invalid(Vec<String>),
}
pub fn validate_registration(request: &RegistrationRequest) -> ValidationResult {
todo!()
}
```
## ステップ 2: テストの作成(RED)
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_registration_returns_valid() {
let request = RegistrationRequest {
name: "Alice".into(),
email: "alice@example.com".into(),
password: "SecureP@ss1".into(),
};
assert!(matches!(validate_registration(&request), ValidationResult::Valid));
}
#[test]
fn blank_name_returns_invalid() {
let request = RegistrationRequest {
name: "".into(),
email: "alice@example.com".into(),
password: "SecureP@ss1".into(),
};
match validate_registration(&request) {
ValidationResult::Invalid(errors) => {
assert!(errors.contains(&"Name is required".to_string()));
}
ValidationResult::Valid => panic!("expected Invalid"),
}
}
#[test]
fn invalid_email_returns_invalid() {
let request = RegistrationRequest {
name: "Alice".into(),
email: "not-an-email".into(),
password: "SecureP@ss1".into(),
};
assert!(matches!(
validate_registration(&request),
ValidationResult::Invalid(_)
));
}
#[test]
fn short_password_returns_invalid() {
let request = RegistrationRequest {
name: "Alice".into(),
email: "alice@example.com".into(),
password: "short".into(),
};
assert!(matches!(
validate_registration(&request),
ValidationResult::Invalid(_)
));
}
}
```
## ステップ 3: テストの実行 — 失敗を確認
```bash
$ cargo test
running 4 tests
test tests::valid_registration_returns_valid ... FAILED (not yet implemented)
test tests::blank_name_returns_invalid ... FAILED
test tests::invalid_email_returns_invalid ... FAILED
test tests::short_password_returns_invalid ... FAILED
```
テストは想定通り失敗しました(todo! パニック)。
## ステップ 4: 最小限のコードを実装(GREEN)
```rust
pub fn validate_registration(request: &RegistrationRequest) -> ValidationResult {
let mut errors = Vec::new();
if request.name.trim().is_empty() {
errors.push("Name is required".into());
}
if !request.email.contains('@') {
errors.push("Invalid email format".into());
}
if request.password.len() < 8 {
errors.push("Password must be at least 8 characters".into());
}
if errors.is_empty() {
ValidationResult::Valid
} else {
ValidationResult::Invalid(errors)
}
}
```
## ステップ 5: テストの実行 — 成功を確認
```bash
$ cargo test
running 4 tests
test tests::valid_registration_returns_valid ... ok
test tests::blank_name_returns_invalid ... ok
test tests::invalid_email_returns_invalid ... ok
test tests::short_password_returns_invalid ... ok
test result: ok. 4 passed; 0 failed
```
すべてのテストが成功しました!
## ステップ 6: カバレッジの確認
```bash
$ cargo llvm-cov
Coverage: 100.0% of lines
```
カバレッジ: 100%
## TDD 完了!
测试模式¶
单元测试¶
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn adds_two_numbers() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn handles_error() -> Result<(), Box<dyn std::error::Error>> {
let result = parse_config(r#"port = 8080"#)?;
assert_eq!(result.port, 8080);
Ok(())
}
}
使用 rstest 的参数化测试¶
use rstest::{rstest, fixture};
#[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());
}
基于属性的测试¶
use proptest::prelude::*;
proptest! {
#[test]
fn encode_decode_roundtrip(input in ".*") {
let encoded = encode(&input);
let decoded = decode(&encoded).unwrap();
assert_eq!(input, decoded);
}
}
覆盖率命令¶
# サマリーレポート
cargo llvm-cov
# HTMLレポート
cargo llvm-cov --html
# しきい値を下回った場合に失敗
cargo llvm-cov --fail-under-lines 80
# 特定のテストを実行
cargo test test_name
# 出力付きで実行
cargo test -- --nocapture
# 最初の失敗で停止しない
cargo test --no-fail-fast
覆盖率目标¶
| 代码类型 | 目标 |
|---|---|
| 关键业务逻辑 | 100% |
| 公共 API | 90% 以上 |
| 一般代码 | 80% 以上 |
| 生成代码 / FFI 绑定 | 排除 |
TDD 最佳实践¶
应该做:
- 在实现之前先编写测试
- 每次更改后运行测试
- 为了获得更好的错误消息,使用 assert_eq! 而不是 assert!
- 为了获得更干净的输出,在返回 Result 的测试中使用 ?
- 测试行为而不是实现
- 包含边缘情况(空值、边界值、错误路径)
不应该做:
- 在测试之前编写实现
- 跳过 RED 阶段
- 在 Result::is_err() 可以处理的情况下使用 #[should_panic]
- 在测试中使用 sleep() — 使用通道或 tokio::time::pause()
- 将所有内容都 mock 化 — 尽可能优先使用集成测试
相关命令¶
/rust-build- 修复构建错误/rust-review- 实现后的代码审查verification-loop技能 - 运行完整验证循环
相关¶
- 技能:
skills/rust-testing/ - 技能:
skills/rust-patterns/
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC