mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 21:53:28 +08:00
6.9 KiB
6.9 KiB
description
| description |
|---|
| 为Rust强制执行TDD工作流。先写测试,然后实现。使用cargo-llvm-cov验证80%以上的覆盖率。 |
Rust TDD 命令
该命令使用 #[test]、rstest、proptest 和 mockall 来强制执行 Rust 代码的测试驱动开发方法。
该命令的作用
- 定义类型/特征:使用
todo!()搭建函数签名 - 编写测试:创建全面的测试模块(RED)
- 运行测试:验证测试因正确的原因而失败
- 实现代码:编写通过测试所需的最少代码(GREEN)
- 重构:在保持测试通过的前提下进行改进
- 检查覆盖率:使用 cargo-llvm-cov 确保 80%+ 的覆盖率
何时使用
在以下情况使用 /rust-test:
- 实现新的 Rust 函数、方法或特征时
- 为现有 Rust 代码添加测试覆盖时
- 修复错误时(首先编写失败的测试)
- 构建关键业务逻辑时
- 学习 Rust 中的 TDD 工作流程时
TDD 循环
RED -> 先编写失败的测试
GREEN -> 实现最小化代码以通过测试
REFACTOR -> 改进代码,保持测试通过
REPEAT -> 下一个测试用例
示例会话
User: /rust-test I need a function to validate user registration
Agent:
# TDD Session: Registration Validator
## Step 1: Define Interface
```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!()
}
```
## Step 2: Write Tests (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(_)
));
}
}
```
## Step 3: Run Tests — Verify FAIL
```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
```
Tests fail as expected (todo! panic).
## Step 4: Implement Minimal Code (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)
}
}
```
## Step 5: Run Tests — Verify PASS
```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
```
All tests passing!
## Step 6: Check Coverage
```bash
$ cargo llvm-cov
Coverage: 100.0% of lines
```
Coverage: 100%
## TDD Complete!
测试模式
单元测试
#[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);
}
}
覆盖率命令
# Summary report
cargo llvm-cov
# HTML report
cargo llvm-cov --html
# Fail if below threshold
cargo llvm-cov --fail-under-lines 80
# Run specific test
cargo test test_name
# Run with output
cargo test -- --nocapture
# Run without stopping on first failure
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() - 模拟一切 — 在可行时优先使用集成测试
相关命令
/rust-build- 修复构建错误/rust-review- 在实现后审查代码/verify- 运行完整的验证循环
相关
- 技能:
skills/rust-testing/ - 技能:
skills/rust-patterns/