| name | rust-testing-code-review |
| description | Reviews Rust test code for unit test patterns, integration test structure, async testing, mocking approaches, and property-based testing. Covers Rust 2024 edition changes including async fn in traits for mocks, |
| metadata | {"author":"existential-birds"} |
Rust Testing Code Review
Review Workflow
- 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
- Check test organization — Unit tests in
#[cfg(test)] modules, integration tests in tests/ directory
- 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
- Check assertions — Meaningful messages, correct assertion type. Review
if let assertions for edition 2024 temporary scope changes
- Check test isolation — No shared mutable state between tests, proper setup/teardown. Prefer
LazyLock over lazy_static!/once_cell for shared fixtures
- Check coverage patterns — Error paths tested, edge cases covered
Gates (hard)
Do not advance to Output Format until each pass condition is satisfied (yes/no with a concrete artifact).
- Edition recorded — Open the target crate’s
Cargo.toml (or workspace [workspace.package] / inherited edition) and note the edition value. Pass: you can quote edition = "…" (or document “inherited from workspace”) before citing Rust 2024–specific behavior (if let / tail temporary drops, #[expect] vs #[allow] migration, native async fn in traits as default). If edition is not 2024, do not report those items as edition-2024 regressions; at most Informational if still useful.
dyn vs static async mocks — Before suggesting native async fn in traits instead of async-trait, check whether the mock is used as dyn Trait. Pass: if dyn is required, you either skip that suggestion or align with Valid Patterns (async-trait still needed).
- Verification protocol — Pass: steps from
beagle-rust:review-verification-protocol are done before any finding is listed (see Before Submitting Findings).
Output Format
Report findings as:
[FILE:LINE] ISSUE_TITLE
Severity: Critical | Major | Minor | Informational
Description of the issue and why it matters.
Quick Reference
| Issue Type | Reference |
|---|
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, proptest, Miri, Loom basics, mocking strategies, stub/fake/mock/spy taxonomy, rstest matrix, paste!, build.rs test gen, criterion baselines + black_box discipline, trybuild UI tests, clippy lint groups | references/advanced-testing.md |
| Loom interleaving tests, Miri UB checks, shuttle, ThreadSanitizer, CI matrix for concurrent code | references/concurrency-testing.md |
Review Checklist
Test Structure
Async Tests
Assertions
Mocking and Test Doubles
Error Path Testing
Lint Suppression in Tests
Test Naming
Snapshot Testing
Parametrized Testing
Doc Tests
Concurrency Testing
Detailed guidance: references/concurrency-testing.md
Test Augmentation (Fakes, Mocks, Stubs, Spies)
Detailed guidance: references/advanced-testing.md
Performance Tests (Criterion)
Detailed guidance: references/advanced-testing.md
Test Generation
Detailed guidance: references/advanced-testing.md
Proc-Macro UI Tests (trybuild)
Detailed guidance: references/advanced-testing.md
Clippy Lint Group Strategy
Detailed guidance: references/advanced-testing.md
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
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
Informational
- Suggestions to add property-based tests via
proptest or quickcheck
- Suggestions to add snapshot testing for complex output
- Coverage improvement opportunities
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
Before Submitting Findings
Load and follow beagle-rust:review-verification-protocol before reporting any issue.
Source: existential-birds/beagle — distributed by TomeVault.