rust-testing-code-review

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Rust Testing Code Review

Rust测试代码审查

Review Workflow

审查流程

  1. Check Rust edition — Note edition in
    Cargo.toml
    (2021 vs 2024). Edition 2024 changes temporary scoping in
    if let
    and tail expressions, and makes
    #[expect]
    the preferred lint suppression
  2. Check test organization — Unit tests in
    #[cfg(test)]
    modules, integration tests in
    tests/
    directory
  3. Check async test setup
    #[tokio::test]
    for async tests, proper runtime configuration. Check for
    async-trait
    on mocks that could use native
    async fn
    in traits
  4. Check assertions — Meaningful messages, correct assertion type. Review
    if let
    assertions for edition 2024 temporary scope changes
  5. Check test isolation — No shared mutable state between tests, proper setup/teardown. Prefer
    LazyLock
    over
    lazy_static!
    /
    once_cell
    for shared fixtures
  6. Check coverage patterns — Error paths tested, edge cases covered
  1. 检查Rust版本 — 记录
    Cargo.toml
    中的版本(2021 vs 2024)。2024版本变更了
    if let
    和尾表达式中的临时作用域,并将
    #[expect]
    设为首选的lint抑制方式
  2. 检查测试组织方式 — 单元测试位于
    #[cfg(test)]
    模块中,集成测试位于
    tests/
    目录下
  3. 检查异步测试设置 — 异步测试使用
    #[tokio::test]
    ,配置正确的运行时。检查Mock是否可以使用原生的trait异步函数,替代
    async-trait
  4. 检查断言 — 断言需包含有意义的信息,使用正确的断言类型。针对2024版本中
    if let
    的临时作用域变更,审查相关断言
  5. 检查测试隔离性 — 测试之间无共享可变状态,拥有正确的初始化/清理逻辑。优先使用
    LazyLock
    而非
    lazy_static!
    /
    once_cell
    来处理共享测试夹具
  6. 检查覆盖模式 — 已测试错误路径,覆盖边缘情况

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 TypeReference
Unit tests, assertions, naming, snapshots, rstest, doc tests,
#[expect]
,
LazyLock
fixtures, tail expression scope
references/unit-tests.md
Integration tests, async testing, fixtures, test databases, native
async fn
mocks,
if let
temporary scope
references/integration-tests.md
Fuzzing, property-based testing, Miri, Loom, benchmarking, compile_fail, custom harness, mocking strategiesreferences/advanced-testing.md
问题类型参考文档
单元测试、断言、命名、快照测试、rstest、文档测试、
#[expect]
LazyLock
测试夹具、尾表达式作用域
references/unit-tests.md
集成测试、异步测试、测试夹具、测试数据库、原生trait异步函数Mock、
if let
临时作用域
references/integration-tests.md
模糊测试、基于属性的测试、Miri、Loom、基准测试、compile_fail、自定义测试 harness、Mock策略references/advanced-testing.md

Review Checklist

审查检查清单

Test Structure

测试结构

  • Unit tests in
    #[cfg(test)] mod tests
    within source files
  • Integration tests in
    tests/
    directory (one file per module or feature)
  • use super::*
    in test modules to access parent module items
  • 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

异步测试

  • #[tokio::test]
    used for async test functions
  • #[tokio::test(flavor = "multi_thread")]
    when testing multi-threaded behavior
  • No
    block_on
    inside async tests (use
    .await
    directly)
  • Test timeouts set for tests that could hang
  • Mock traits use native
    async fn
    instead of
    async-trait
    crate (stable since Rust 1.75)
  • 异步测试函数使用
    #[tokio::test]
  • 测试多线程行为时使用
    #[tokio::test(flavor = "multi_thread")]
  • 异步测试内部不使用
    block_on
    (直接使用
    .await
  • 为可能挂起的测试设置超时
  • Mock trait使用原生
    async fn
    而非
    async-trait
    crate(Rust 1.75起稳定支持)

Assertions

断言

  • assert_eq!
    /
    assert_ne!
    used for value comparisons (better error messages than
    assert!
    )
  • Custom messages on assertions that aren't self-documenting
  • matches!
    macro used for enum variant checking
  • Error types checked with
    matches!
    or pattern matching, not string comparison
  • One assertion per test where practical (easier to diagnose failures)
  • if let
    assertions reviewed for edition 2024 temporary scope — temporaries in conditions drop earlier, may invalidate borrows
  • 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
  • std::sync::LazyLock
    used for shared test fixtures instead of
    lazy_static!
    or
    once_cell
    (stable since Rust 1.80)
  • 使用trait作为依赖注入的接缝(而非具体类型)
  • Mock实现保持最小化 — 仅包含测试所需的功能
  • 不要Mock不属于你维护的类型(将外部依赖封装在自己的trait后再Mock)
  • 测试夹具以辅助函数形式实现,而非全局状态
  • 使用
    std::sync::LazyLock
    处理共享测试夹具,替代
    lazy_static!
    once_cell
    (Rust 1.80起稳定支持)

Error Path Testing

错误路径测试

  • Result::Err
    variants tested, not just happy paths
  • Specific error variants checked (not just "is error")
  • #[should_panic]
    used sparingly — prefer
    Result
    -returning tests
  • 已测试
    Result::Err
    变体,而非仅测试正常路径
  • 检查具体的错误变体(而非仅判断“是否为错误”)
  • 谨慎使用
    #[should_panic]
    — 优先使用返回
    Result
    的测试

Lint Suppression in Tests

测试中的Lint抑制

  • #[expect(lint)]
    used instead of
    #[allow(lint)]
    for test-specific suppressions (stable since Rust 1.81)
  • Justification comment on every
    #[expect]
    or
    #[allow]
    in test code
  • Stale
    #[allow]
    attributes migrated to
    #[expect]
    for self-cleaning behavior
  • 针对测试特有的抑制,使用
    #[expect(lint)]
    而非
    #[allow(lint)]
    (Rust 1.81起稳定支持)
  • 测试代码中的每个
    #[expect]
    #[allow]
    都要有注释说明理由
  • 将过时的
    #[allow]
    属性迁移为
    #[expect]
    ,以实现自清理

Test Naming

测试命名

  • Test names read like sentences describing behavior (not
    test_happy_path
    )
  • Related tests grouped in nested
    mod
    blocks for organization
  • Test names follow pattern:
    <function>_should_<behavior>_when_<condition>
  • 测试名称读起来像描述行为的句子(而非
    test_happy_path
  • 相关测试分组在嵌套
    mod
    块中,便于组织
  • 测试名称遵循模式:
    <函数名>_should_<行为>_when_<条件>

Snapshot Testing

快照测试

  • cargo insta
    used for complex structural output (JSON, YAML, HTML, CLI output)
  • Snapshots are small and focused (not huge objects)
  • Redactions used for unstable fields (timestamps, UUIDs)
  • Snapshots committed to git in
    snapshots/
    directory
  • Simple values use
    assert_eq!
    , not snapshots
  • 使用
    cargo insta
    处理复杂的结构化输出(JSON、YAML、HTML、CLI输出)
  • 快照保持小巧且聚焦(而非大型对象)
  • 对不稳定字段(时间戳、UUID)使用脱敏处理
  • 快照提交至git的
    snapshots/
    目录中
  • 简单值使用
    assert_eq!
    ,而非快照测试

Parametrized Testing

参数化测试

  • rstest
    used to avoid duplicated test functions for similar inputs
  • #[rstest]
    with
    #[case::name]
    attributes for descriptive parametrized tests
  • #[fixture]
    used for shared test setup when multiple tests need same construction
  • Parametrized tests still have descriptive case names (not just
    #[case(1)]
    )
  • Combined with async:
    #[rstest] #[tokio::test]
    for async parametrized tests
  • 使用
    rstest
    避免为相似输入编写重复的测试函数
  • 结合
    #[rstest]
    #[case::名称]
    属性,实现描述性的参数化测试
  • 当多个测试需要相同的初始化逻辑时,使用
    #[fixture]
    处理共享设置
  • 参数化测试仍要有描述性的用例名称(而非仅
    #[case(1)]
  • 与异步结合:使用
    #[rstest] #[tokio::test]
    实现异步参数化测试

Doc Tests

文档测试

  • Public API functions have
    /// # Examples
    with runnable code
  • Doc tests serve as both documentation and correctness checks
  • Hidden setup lines prefixed with
    #
    to keep examples clean
  • cargo test --doc
    passes (nextest doesn't run doc tests)
  • 公共API函数包含
    /// # Examples
    注释,且示例代码可运行
  • 文档测试同时作为文档和正确性检查
  • 隐藏的设置行以
    #
    为前缀,保持示例简洁
  • cargo test --doc
    执行通过(nextest不会运行文档测试)

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

主要

  • #[should_panic]
    without
    expected
    message (catches any panic, including wrong ones)
  • unwrap()
    in test setup that hides the real failure location
  • Tests that depend on execution order
  • if let
    with inline temporary in assertion that breaks under edition 2024 temporary scoping
  • async-trait
    on mock traits when native
    async fn
    in traits is available and project targets edition 2024
  • #[should_panic]
    未指定
    expected
    消息(会捕获任何panic,包括非预期的panic)
  • 测试设置中使用
    unwrap()
    隐藏了真正的失败位置
  • 测试依赖执行顺序
  • 断言中使用带内联临时变量的
    if let
    ,在2024版本中会因临时作用域变更而失效
  • 项目目标为2024版本时,Mock trait仍使用
    async-trait
    而非原生trait异步函数

Minor

次要

  • Missing assertion messages on complex comparisons
  • assert!(x == y)
    instead of
    assert_eq!(x, y)
    (worse error messages)
  • Test names that don't describe the scenario
  • Redundant setup code that could be extracted to a helper
  • #[allow]
    used where
    #[expect]
    would provide self-cleaning suppression
  • lazy_static!
    or
    once_cell
    used for test fixtures when
    LazyLock
    is available
  • 复杂比较缺少断言消息
  • 使用
    assert!(x == y)
    而非
    assert_eq!(x, y)
    (错误信息更差)
  • 测试名称未描述场景
  • 可提取为辅助函数的冗余设置代码
  • 在可使用
    #[expect]
    的场景下使用了
    #[allow]
  • 可使用
    LazyLock
    时仍使用
    lazy_static!
    once_cell
    处理测试夹具

Informational

信息性

  • Suggestions to add property-based tests via
    proptest
    or
    quickcheck
  • Suggestions to add snapshot testing for complex output
  • Coverage improvement opportunities
  • 建议通过
    proptest
    quickcheck
    添加基于属性的测试
  • 建议为复杂输出添加快照测试
  • 测试覆盖优化建议

Valid Patterns (Do NOT Flag)

有效模式(无需标记)

  • unwrap()
    /
    expect()
    in tests
    — Panicking on unexpected errors is the correct test behavior
  • use super::*
    in test modules
    — Standard pattern for accessing parent items
  • #[allow(dead_code)]
    on test helpers
    — Helper functions may not be used in every test
  • clone()
    in tests
    — Clarity over performance
  • Large test functions — Integration tests can be long; extracting helpers isn't always clearer
  • assert!
    for boolean checks
    — Fine when the expression is clearly boolean (
    .is_some()
    ,
    .is_empty()
    )
  • Multiple assertions testing one logical behavior — Sometimes one behavior needs multiple checks
  • unwrap()
    on
    Result
    -returning test functions
    — Propagating with
    ?
    is also fine but not required
  • async-trait
    on mock traits requiring
    dyn
    dispatch
    — Native
    async fn
    in traits doesn't support
    dyn Trait
    ;
    async-trait
    is still needed there
  • #[expect]
    with justification on test helpers
    — Self-cleaning lint suppression is correct in test code
  • LazyLock
    for expensive shared test fixtures
    — Thread-safe lazy init is appropriate for test globals
  • 测试中的
    unwrap()
    /
    expect()
    — 在遇到意外错误时触发panic是正确的测试行为
  • 测试模块中的
    use super::*
    — 这是访问父模块项的标准模式
  • 测试辅助函数上的
    #[allow(dead_code)]
    — 辅助函数可能不会在所有测试中被使用
  • 测试中的
    clone()
    — 优先保证清晰性而非性能
  • 大型测试函数 — 集成测试可以较长;提取辅助函数并不总是更清晰
  • 布尔检查使用
    assert!
    — 当表达式明确为布尔值时(如
    .is_some()
    .is_empty()
    ),使用
    assert!
    是合适的
  • 单个逻辑行为对应多个断言 — 有时一个行为需要多个检查
  • 返回
    Result
    的测试函数中使用
    unwrap()
    — 使用
    ?
    传播错误也是可行的,但并非必须
  • 需要
    dyn
    调度的Mock trait上使用
    async-trait
    — 原生trait异步函数不支持
    dyn Trait
    ;此时仍需使用
    async-trait
  • 测试辅助函数上带理由的
    #[expect]
    — 测试代码中使用自清理的lint抑制是正确的做法
  • 昂贵的共享测试夹具使用
    LazyLock
    — 线程安全的延迟初始化适用于测试全局变量

Before Submitting Findings

提交结果前

Load and follow
beagle-rust:review-verification-protocol
before reporting any issue.
在报告任何问题前,需加载并遵循
beagle-rust:review-verification-protocol