Skills rust-testing-code-review
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, #[expect] lint suppression, LazyLock test fixtures, and temporary scope changes affecting test assertions. Use when reviewing _test.rs files, #[cfg(test)] modules, or test infrastructure in Rust projects. Covers tokio::test, test fixtures, and assertion patterns.
install
source · Clone the upstream repo
git clone https://github.com/openclaw/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/anderskev/rust-testing-code-review" ~/.claude/skills/openclaw-skills-rust-testing-code-review && rm -rf "$T"
OpenClaw · Install into ~/.openclaw/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.openclaw/skills && cp -r "$T/skills/anderskev/rust-testing-code-review" ~/.openclaw/skills/openclaw-skills-rust-testing-code-review && rm -rf "$T"
manifest:
skills/anderskev/rust-testing-code-review/SKILL.mdsource content
Rust Testing Code Review
Review Workflow
- Check Rust edition — Note edition in
(2021 vs 2024). Edition 2024 changes temporary scoping inCargo.toml
and tail expressions, and makesif let
the 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-trait
in traitsasync fn - Check assertions — Meaningful messages, correct assertion type. Review
assertions for edition 2024 temporary scope changesif let - Check test isolation — No shared mutable state between tests, proper setup/teardown. Prefer
overLazyLock
/lazy_static!
for shared fixturesonce_cell - Check coverage patterns — Error paths tested, edge cases covered
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, , fixtures, tail expression scope | references/unit-tests.md |
Integration tests, async testing, fixtures, test databases, native mocks, temporary scope | references/integration-tests.md |
| Fuzzing, property-based testing, Miri, Loom, benchmarking, compile_fail, custom harness, mocking strategies | 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 itemsuse super::* - Test function names describe the scenario:
test_<function>_<scenario>_<expected> - Tests are independent — no reliance on execution order
Async Tests
-
used for async test functions#[tokio::test] -
when testing multi-threaded behavior#[tokio::test(flavor = "multi_thread")] - No
inside async tests (useblock_on
directly).await - Test timeouts set for tests that could hang
- Mock traits use native
instead ofasync fn
crate (stable since 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 checkingmatches! - Error types checked with
or pattern matching, not string comparisonmatches! - One assertion per test where practical (easier to diagnose failures)
-
assertions reviewed for edition 2024 temporary scope — temporaries in conditions drop earlier, may invalidate borrowsif let - Tail expression returns reviewed for edition 2024 — temporaries in tail expressions drop before local variables
Mocking and Test Doubles
- 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 ofstd::sync::LazyLock
orlazy_static!
(stable since Rust 1.80)once_cell
Error Path Testing
-
variants tested, not just happy pathsResult::Err - Specific error variants checked (not just "is error")
-
used sparingly — prefer#[should_panic]
-returning testsResult
Lint Suppression in Tests
-
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]
Test Naming
- Test names read like sentences describing behavior (not
)test_happy_path - Related tests grouped in nested
blocks for organizationmod - Test names follow pattern:
<function>_should_<behavior>_when_<condition>
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
directorysnapshots/ - Simple values use
, not snapshotsassert_eq!
Parametrized Testing
-
used to avoid duplicated test functions for similar inputsrstest -
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]
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
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 locationunwrap()- Tests that depend on execution order
with inline temporary in assertion that breaks under edition 2024 temporary scopingif let
on mock traits when nativeasync-trait
in traits is available and project targets edition 2024async fn
Minor
- Missing assertion messages on complex comparisons
instead ofassert!(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]
orlazy_static!
used for test fixtures whenonce_cell
is availableLazyLock
Informational
- Suggestions to add property-based tests via
orproptestquickcheck - Suggestions to add snapshot testing for complex output
- Coverage improvement opportunities
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 itemsuse super::*
on test helpers — Helper functions may not be used in every test#[allow(dead_code)]
in tests — Clarity over performanceclone()- 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
onunwrap()
-returning test functions — Propagating withResult
is also fine but not required?
on mock traits requiringasync-trait
dispatch — Nativedyn
in traits doesn't supportasync fn
;dyn Trait
is 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 globalsLazyLock
Before Submitting Findings
Load and follow
beagle-rust:review-verification-protocol before reporting any issue.