All Rust templates

Rust Testing Patterns

Unit tests, integration tests, doctests, proptest, and CI patterns.

DevZone Tools880 copiesUpdated Apr 23, 2026Rust
# CLAUDE.md — Rust Testing Patterns

## Test layout

- **Unit tests** live in the same file as the code, in a `#[cfg(test)] mod tests { ... }` block.
- **Integration tests** live in `tests/` at the crate root. Each file is a separate binary.
- **Doc tests** live in doc comments, run by `cargo test`.
- **Benchmarks** in `benches/` with **criterion** for stable measurements.

## Running

- `cargo test` runs everything. `cargo test --lib` for unit tests only.
- `cargo test <pattern>` filters by name. Useful when iterating.
- `cargo nextest run` is faster than `cargo test` for medium-to-large suites. Worth installing.

## Assertions

- `assert!`, `assert_eq!`, `assert_ne!` from std are enough most of the time.
- For richer output, **pretty_assertions** crate — `pretty_assertions::assert_eq!` shows colored diffs.
- For complex shapes, **insta** for snapshot tests:
  ```rust
  insta::assert_yaml_snapshot!(result);
  ```

## Test fixtures

- For shared setup, write helper functions in a `tests/common/mod.rs` (not `tests/common.rs` — that runs as a test binary).
- Don't share mutable state across tests. They run in parallel by default.

## Async tests

- `#[tokio::test]` on `async fn` test functions. Needs the `tokio` test feature: `tokio = { ..., features = ["macros", "rt-multi-thread"] }`.
- For multi-thread runtime: `#[tokio::test(flavor = "multi_thread")]`.
- `#[tokio::test(start_paused = true)]` to control time deterministically.

## Property tests

- **proptest** or **quickcheck** for generated inputs:
  ```rust
  proptest! {
      #[test]
      fn parse_roundtrip(s in "[a-z]+") {
          assert_eq!(parse(&serialize(&s)).unwrap(), s);
      }
  }
  ```
- Property tests catch edge cases unit tests miss. Use them for parsers, serializers, math.

## Mocking

- **mockall** for mock implementations of traits. Generate from `#[automock]`.
- Don't mock concrete types — refactor to traits at the boundary, then mock those.
- For HTTP, use **wiremock** or **mockito** to stand up a fake server in-process.

## Database tests

- Use **testcontainers-rs** to spin up real Postgres/Redis/whatever. The boot cost is amortized over many tests.
- Each test creates its own schema or uses a transaction that rolls back at the end.
- Don't fake the database. Bugs hide in the impedance.

## Coverage

- `cargo llvm-cov` (or `cargo tarpaulin`) for coverage. Aim for high coverage on logic, not on every match arm.
- Coverage targets are signals, not goals. A 95% covered codebase with no integration tests has worse properties than 70% covered with good ones.

## CI

- `cargo test --all-features` and `cargo test --no-default-features` separately. Feature combinations are where bugs hide.
- `cargo clippy --all-targets --all-features -- -D warnings` is mandatory.
- Cache `target/` between runs. Rust builds are slow without it.

## Don't

- Don't `.unwrap()` everything in tests. `expect("explanation")` so failures point at the cause.
- Don't sleep in tests for timing. Use mocked time or signals.
- Don't depend on test execution order. They run in parallel; assume any order.
- Don't disable warnings to make a test compile. Fix the warning.

Other Rust templates