rust-testing-code-review
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRust Testing Code Review
Rust测试代码审查
Review Workflow
审查流程
- Check Rust edition — Note edition in (2021 vs 2024). Edition 2024 changes temporary scoping in
Cargo.tomland tail expressions, and makesif letthe preferred lint suppression#[expect] - Check test organization — Unit tests in modules, integration tests in
#[cfg(test)]directorytests/ - Check async test setup — for async tests, proper runtime configuration. Check for
#[tokio::test]on mocks that could use nativeasync-traitin traitsasync fn - Check assertions — Meaningful messages, correct assertion type. Review assertions for edition 2024 temporary scope changes
if let - Check test isolation — No shared mutable state between tests, proper setup/teardown. Prefer over
LazyLock/lazy_static!for shared fixturesonce_cell - Check coverage patterns — Error paths tested, edge cases covered
- 检查Rust版本 — 记录中的版本(2021 vs 2024)。2024版本变更了
Cargo.toml和尾表达式中的临时作用域,并将if let设为首选的lint抑制方式#[expect] - 检查测试组织方式 — 单元测试位于模块中,集成测试位于
#[cfg(test)]目录下tests/ - 检查异步测试设置 — 异步测试使用,配置正确的运行时。检查Mock是否可以使用原生的trait异步函数,替代
#[tokio::test]async-trait - 检查断言 — 断言需包含有意义的信息,使用正确的断言类型。针对2024版本中的临时作用域变更,审查相关断言
if let - 检查测试隔离性 — 测试之间无共享可变状态,拥有正确的初始化/清理逻辑。优先使用而非
LazyLock/lazy_static!来处理共享测试夹具once_cell - 检查覆盖模式 — 已测试错误路径,覆盖边缘情况
Output Format
输出格式
Report findings as:
text
[FILE:LINE] ISSUE_TITLE
Severity: Critical | Major | Minor | Informational
Description of the issue and why it matters.按照以下格式报告审查结果:
text
[文件:行号] 问题标题
严重程度: 关键 | 主要 | 次要 | 信息性
问题描述及其影响。Quick Reference
快速参考
| Issue Type | Reference |
|---|---|
Unit tests, assertions, naming, snapshots, rstest, doc tests, | references/unit-tests.md |
Integration tests, async testing, fixtures, test databases, native | references/integration-tests.md |
| Fuzzing, property-based testing, Miri, Loom, benchmarking, compile_fail, custom harness, mocking strategies | references/advanced-testing.md |
| 问题类型 | 参考文档 |
|---|---|
单元测试、断言、命名、快照测试、rstest、文档测试、 | references/unit-tests.md |
集成测试、异步测试、测试夹具、测试数据库、原生trait异步函数Mock、 | references/integration-tests.md |
| 模糊测试、基于属性的测试、Miri、Loom、基准测试、compile_fail、自定义测试 harness、Mock策略 | references/advanced-testing.md |
Review Checklist
审查检查清单
Test Structure
测试结构
- Unit tests in within source files
#[cfg(test)] mod tests - Integration tests in directory (one file per module or feature)
tests/ - in test modules to access parent module items
use super::* - Test function names describe the scenario:
test_<function>_<scenario>_<expected> - Tests are independent — no reliance on execution order
- 单元测试位于源文件内的模块中
#[cfg(test)] mod tests - 集成测试位于目录下(每个模块或功能对应一个文件)
tests/ - 测试模块中使用来访问父模块项
use super::* - 测试函数名称能够描述场景:
test_<函数名>_<场景>_<预期结果> - 测试相互独立 — 不依赖执行顺序
Async Tests
异步测试
- used for async test functions
#[tokio::test] - when testing multi-threaded behavior
#[tokio::test(flavor = "multi_thread")] - No inside async tests (use
block_ondirectly).await - Test timeouts set for tests that could hang
- Mock traits use native instead of
async fncrate (stable since Rust 1.75)async-trait
- 异步测试函数使用
#[tokio::test] - 测试多线程行为时使用
#[tokio::test(flavor = "multi_thread")] - 异步测试内部不使用(直接使用
block_on).await - 为可能挂起的测试设置超时
- Mock trait使用原生而非
async fncrate(Rust 1.75起稳定支持)async-trait
Assertions
断言
- /
assert_eq!used for value comparisons (better error messages thanassert_ne!)assert! - Custom messages on assertions that aren't self-documenting
- macro used for enum variant checking
matches! - Error types checked with or pattern matching, not string comparison
matches! - One assertion per test where practical (easier to diagnose failures)
- assertions reviewed for edition 2024 temporary scope — temporaries in conditions drop earlier, may invalidate borrows
if let - Tail expression returns reviewed for edition 2024 — temporaries in tail expressions drop before local variables
- 使用/
assert_eq!进行值比较(比assert_ne!提供更清晰的错误信息)assert! - 非自解释的断言需添加自定义信息
- 使用宏检查枚举变体
matches! - 使用或模式匹配检查错误类型,而非字符串比较
matches! - 尽可能每个测试只包含一个断言(便于诊断失败原因)
- 针对2024版本的临时作用域变更,审查相关断言 — 条件中的临时变量会更早被销毁,可能导致借用失效
if let - 针对2024版本的尾表达式返回值变更,审查相关代码 — 尾表达式中的临时变量会在局部变量之前被销毁
Mocking and Test Doubles
Mock与测试替身
- Traits used as seams for dependency injection (not concrete types)
- Mock implementations kept minimal — only what the test needs
- No mocking of types you don't own (wrap external dependencies behind your own trait)
- Test fixtures as helper functions, not global state
- used for shared test fixtures instead of
std::sync::LazyLockorlazy_static!(stable since Rust 1.80)once_cell
- 使用trait作为依赖注入的接缝(而非具体类型)
- Mock实现保持最小化 — 仅包含测试所需的功能
- 不要Mock不属于你维护的类型(将外部依赖封装在自己的trait后再Mock)
- 测试夹具以辅助函数形式实现,而非全局状态
- 使用处理共享测试夹具,替代
std::sync::LazyLock或lazy_static!(Rust 1.80起稳定支持)once_cell
Error Path Testing
错误路径测试
- variants tested, not just happy paths
Result::Err - Specific error variants checked (not just "is error")
- used sparingly — prefer
#[should_panic]-returning testsResult
- 已测试变体,而非仅测试正常路径
Result::Err - 检查具体的错误变体(而非仅判断“是否为错误”)
- 谨慎使用— 优先使用返回
#[should_panic]的测试Result
Lint Suppression in Tests
测试中的Lint抑制
- used instead of
#[expect(lint)]for test-specific suppressions (stable since Rust 1.81)#[allow(lint)] - Justification comment on every or
#[expect]in test code#[allow] - Stale attributes migrated to
#[allow]for self-cleaning behavior#[expect]
- 针对测试特有的抑制,使用而非
#[expect(lint)](Rust 1.81起稳定支持)#[allow(lint)] - 测试代码中的每个或
#[expect]都要有注释说明理由#[allow] - 将过时的属性迁移为
#[allow],以实现自清理#[expect]
Test Naming
测试命名
- Test names read like sentences describing behavior (not )
test_happy_path - Related tests grouped in nested blocks for organization
mod - Test names follow pattern:
<function>_should_<behavior>_when_<condition>
- 测试名称读起来像描述行为的句子(而非)
test_happy_path - 相关测试分组在嵌套块中,便于组织
mod - 测试名称遵循模式:
<函数名>_should_<行为>_when_<条件>
Snapshot Testing
快照测试
- used for complex structural output (JSON, YAML, HTML, CLI output)
cargo insta - Snapshots are small and focused (not huge objects)
- Redactions used for unstable fields (timestamps, UUIDs)
- Snapshots committed to git in directory
snapshots/ - Simple values use , not snapshots
assert_eq!
- 使用处理复杂的结构化输出(JSON、YAML、HTML、CLI输出)
cargo insta - 快照保持小巧且聚焦(而非大型对象)
- 对不稳定字段(时间戳、UUID)使用脱敏处理
- 快照提交至git的目录中
snapshots/ - 简单值使用,而非快照测试
assert_eq!
Parametrized Testing
参数化测试
- used to avoid duplicated test functions for similar inputs
rstest - with
#[rstest]attributes for descriptive parametrized tests#[case::name] - used for shared test setup when multiple tests need same construction
#[fixture] - Parametrized tests still have descriptive case names (not just )
#[case(1)] - Combined with async: for async parametrized tests
#[rstest] #[tokio::test]
- 使用避免为相似输入编写重复的测试函数
rstest - 结合与
#[rstest]属性,实现描述性的参数化测试#[case::名称] - 当多个测试需要相同的初始化逻辑时,使用处理共享设置
#[fixture] - 参数化测试仍要有描述性的用例名称(而非仅)
#[case(1)] - 与异步结合:使用实现异步参数化测试
#[rstest] #[tokio::test]
Doc Tests
文档测试
- Public API functions have with runnable code
/// # Examples - Doc tests serve as both documentation and correctness checks
- Hidden setup lines prefixed with to keep examples clean
# - passes (nextest doesn't run doc tests)
cargo test --doc
- 公共API函数包含注释,且示例代码可运行
/// # Examples - 文档测试同时作为文档和正确性检查
- 隐藏的设置行以为前缀,保持示例简洁
# - 执行通过(nextest不会运行文档测试)
cargo test --doc
Severity Calibration
严重程度校准
Critical
关键
- Tests that pass but don't actually verify behavior (assertions on wrong values)
- Shared mutable state between tests causing flaky results
- Missing error path tests for security-critical code
- 测试通过但未实际验证行为(断言错误的值)
- 测试之间共享可变状态导致结果不稳定
- 安全关键代码缺少错误路径测试
Major
主要
- without
#[should_panic]message (catches any panic, including wrong ones)expected - in test setup that hides the real failure location
unwrap() - Tests that depend on execution order
- with inline temporary in assertion that breaks under edition 2024 temporary scoping
if let - on mock traits when native
async-traitin traits is available and project targets edition 2024async fn
- 未指定
#[should_panic]消息(会捕获任何panic,包括非预期的panic)expected - 测试设置中使用隐藏了真正的失败位置
unwrap() - 测试依赖执行顺序
- 断言中使用带内联临时变量的,在2024版本中会因临时作用域变更而失效
if let - 项目目标为2024版本时,Mock trait仍使用而非原生trait异步函数
async-trait
Minor
次要
- Missing assertion messages on complex comparisons
- instead of
assert!(x == y)(worse error messages)assert_eq!(x, y) - Test names that don't describe the scenario
- Redundant setup code that could be extracted to a helper
- used where
#[allow]would provide self-cleaning suppression#[expect] - or
lazy_static!used for test fixtures whenonce_cellis availableLazyLock
- 复杂比较缺少断言消息
- 使用而非
assert!(x == y)(错误信息更差)assert_eq!(x, y) - 测试名称未描述场景
- 可提取为辅助函数的冗余设置代码
- 在可使用的场景下使用了
#[expect]#[allow] - 可使用时仍使用
LazyLock或lazy_static!处理测试夹具once_cell
Informational
信息性
- Suggestions to add property-based tests via or
proptestquickcheck - Suggestions to add snapshot testing for complex output
- Coverage improvement opportunities
- 建议通过或
proptest添加基于属性的测试quickcheck - 建议为复杂输出添加快照测试
- 测试覆盖优化建议
Valid Patterns (Do NOT Flag)
有效模式(无需标记)
- /
unwrap()in tests — Panicking on unexpected errors is the correct test behaviorexpect() - in test modules — Standard pattern for accessing parent items
use super::* - on test helpers — Helper functions may not be used in every test
#[allow(dead_code)] - in tests — Clarity over performance
clone() - Large test functions — Integration tests can be long; extracting helpers isn't always clearer
- for boolean checks — Fine when the expression is clearly boolean (
assert!,.is_some()).is_empty() - Multiple assertions testing one logical behavior — Sometimes one behavior needs multiple checks
- on
unwrap()-returning test functions — Propagating withResultis also fine but not required? - on mock traits requiring
async-traitdispatch — Nativedynin traits doesn't supportasync fn;dyn Traitis still needed thereasync-trait - with justification on test helpers — Self-cleaning lint suppression is correct in test code
#[expect] - for expensive shared test fixtures — Thread-safe lazy init is appropriate for test globals
LazyLock
- 测试中的/
unwrap()— 在遇到意外错误时触发panic是正确的测试行为expect() - 测试模块中的— 这是访问父模块项的标准模式
use super::* - 测试辅助函数上的— 辅助函数可能不会在所有测试中被使用
#[allow(dead_code)] - 测试中的— 优先保证清晰性而非性能
clone() - 大型测试函数 — 集成测试可以较长;提取辅助函数并不总是更清晰
- 布尔检查使用— 当表达式明确为布尔值时(如
assert!、.is_some()),使用.is_empty()是合适的assert! - 单个逻辑行为对应多个断言 — 有时一个行为需要多个检查
- 返回的测试函数中使用
Result— 使用unwrap()传播错误也是可行的,但并非必须? - 需要调度的Mock trait上使用
dyn— 原生trait异步函数不支持async-trait;此时仍需使用dyn Traitasync-trait - 测试辅助函数上带理由的— 测试代码中使用自清理的lint抑制是正确的做法
#[expect] - 昂贵的共享测试夹具使用— 线程安全的延迟初始化适用于测试全局变量
LazyLock
Before Submitting Findings
提交结果前
Load and follow before reporting any issue.
beagle-rust:review-verification-protocol在报告任何问题前,需加载并遵循。
beagle-rust:review-verification-protocol