rtk-tdd

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Rust TDD Workflow

Rust TDD工作流

Three Laws of TDD

TDD三大定律

  1. Do NOT write production code without a failing test
  2. Write only enough test to fail (including compilation failure)
  3. Write only enough production code to pass the failing test
Cycle: RED (test fails) -> GREEN (minimum to pass) -> REFACTOR (cleanup, cargo test)
  1. 没有失败的测试时,不要编写生产代码
  2. 仅编写足够触发失败的测试(包括编译失败)
  3. 仅编写足够让失败测试通过的生产代码
循环:RED(测试失败) -> GREEN(用最少代码通过测试) -> REFACTOR(清理代码,执行cargo test)

Red-Green-Refactor Steps

红-绿-重构步骤

1. Write test in #[cfg(test)] mod tests of the SAME file
2. cargo test MODULE::tests::test_name  -- must FAIL (red)
3. Implement the minimum in the function
4. cargo test MODULE::tests::test_name  -- must PASS (green)
5. Refactor if needed, re-run cargo test (still green)
6. cargo fmt && cargo clippy --all-targets && cargo test  (final gate)
Never skip step 2. If the test passes immediately, it tests nothing.
1. Write test in #[cfg(test)] mod tests of the SAME file
2. cargo test MODULE::tests::test_name  -- must FAIL (red)
3. Implement the minimum in the function
4. cargo test MODULE::tests::test_name  -- must PASS (green)
5. Refactor if needed, re-run cargo test (still green)
6. cargo fmt && cargo clippy --all-targets && cargo test  (final gate)
绝对不要跳过第2步。如果测试直接通过,说明它没有测试任何有效逻辑。

Idiomatic Rust Test Patterns

符合Rust惯用风格的测试模式

PatternUsageWhen
Arrange-Act-AssertBase structure for every testAlways
assert_eq!
/
assert!
Direct comparison / booleansDeterministic values
assert!(result.is_err())
Error path testingInvalid inputs
Result<()>
return type
Tests with
?
operator
Fallible functions
#[should_panic]
Expected panicInvariants, preconditions
tempfile::NamedTempFile
File/I/O testsFilesystem-dependent code
模式用法适用场景
Arrange-Act-Assert所有测试的基础结构所有场景
assert_eq!
/
assert!
直接比较 / 布尔值校验确定性值校验
assert!(result.is_err())
错误路径测试无效输入场景
Result<()>
返回类型
配合
?
运算符编写测试
可失败函数
#[should_panic]
预期会发生panic的场景不变量、前置条件校验
tempfile::NamedTempFile
文件/I/O测试依赖文件系统的代码

Patterns by Code Type

按代码类型划分的测试模式

Code TypeTest PatternExample
Pure function (str -> str)Input literal -> assert output
assert_eq!(truncate("hello", 3), "...")
Parsing/filteringRaw string -> filter -> contains/not-contains
assert!(filter(raw).contains("expected"))
Validation/securityBoundary inputs -> assert bool
assert!(!is_valid("../etc/passwd"))
Error handlingBad input ->
is_err()
assert!(parse("garbage").is_err())
Struct/enum roundtripConstruct -> serialize -> deserialize -> eq
assert_eq!(from_str(to_str(x)), x)
代码类型测试模式示例
纯函数 (str -> str)输入字面量 -> 断言输出
assert_eq!(truncate("hello", 3), "...")
解析/过滤原始字符串 -> 过滤 -> 包含/不包含校验
assert!(filter(raw).contains("expected"))
校验/安全边界输入 -> 断言布尔值
assert!(!is_valid("../etc/passwd"))
错误处理错误输入 ->
is_err()
校验
assert!(parse("garbage").is_err())
结构体/枚举序列化往返构造 -> 序列化 -> 反序列化 -> 相等校验
assert_eq!(from_str(to_str(x)), x)

Naming Convention

命名规范

test_{function}_{scenario}
test_{function}_{input_type}
Examples:
test_truncate_edge_case
,
test_parse_invalid_input
,
test_filter_empty_string
test_{function}_{scenario}
test_{function}_{input_type}
示例:
test_truncate_edge_case
test_parse_invalid_input
test_filter_empty_string

When NOT to Use Pure TDD

不适合使用纯TDD的场景

  • Functions calling
    Command::new()
    -> test the parser, not the execution
  • std::process::exit()
    -> refactor to
    Result
    first, then test the Result
  • Direct I/O (SQLite, network) -> use tempfile/mock or test the pure logic separately
  • Main/CLI wiring -> covered by integration/smoke tests
  • 调用
    Command::new()
    的函数 -> 测试解析逻辑,而非执行逻辑
  • 包含
    std::process::exit()
    的代码 -> 先重构为返回
    Result
    ,再测试返回的Result
  • 直接I/O操作(SQLite、网络请求) -> 使用临时文件/Mock或者单独测试纯逻辑部分
  • 主函数/CLI拼接逻辑 -> 由集成/冒烟测试覆盖

Pre-Commit Gate

预提交门禁检查

bash
cargo fmt --all --check
cargo clippy --all-targets
cargo test
All 3 must pass. No exceptions. No
#[allow(...)]
without documented justification.
bash
cargo fmt --all --check
cargo clippy --all-targets
cargo test
三项必须全部通过,无例外。没有书面说明的情况下,不允许添加
#[allow(...)]
注解。