Rust Testing and Benchmarking
Based on Chapter 11 of The Rust Programming Language and the Rust Book.
Capability Boundaries
✅ Strengths
- Unit tests (using
#[test], organizing test modules with #[cfg(test)])
- Assertion macros (
assert!, assert_eq!, assert_ne!, debug_assert!)
- Test attributes (
#[should_panic], #[ignore], #[cfg(test)])
- Integration tests (tests/ directory and shared modules)
- Documentation tests (code blocks, hidden lines with
#, should_panic/no_run/ignore flags)
- cargo test runner (filtering, --nocapture, --test-threads, --include-ignored options)
- Stable Criterion benchmarks; explicitly distinguishing stable Criterion from nightly-only libtest
#[bench] attribute
- Code coverage using cargo-llvm-cov
- Asynchronous race conditions, backpressure, timeouts, process/daemon models, platform matrices, and resource-constrained testing
⚠️ Prerequisites
- Understanding of Rust module system (rust-workspace)
❌ Out of Scope
- Property-based tests (
proptest) → Not currently covered
- Mock objects → Not currently covered
- Basic Rust syntax → Use
rust-stable skill instead
When to Use
- "Write unit tests"
- "Where should integration tests be placed?"
- "How do I write documentation tests?"
- "Performance benchmarking"
- "Check code coverage"
Routing Boundary
Use rust-java-migration-testing when tests must disposition a source Java suite, distinguish mirrored tests from golden/live differential evidence, add target-specific ownership/async/error/component obligations, audit coverage-chasing tests, or verify migration lifecycle/adapter/host acceptance. Keep this skill focused on general Rust test mechanics and Rust-native test architecture.
Unit Tests
// src/lib.rs
pub fn add(a: i32, b: i32) -> i32 { a + b }
pub fn divide(a: i32, b: i32) -> i32 {
if b == 0 { panic!("divide by zero"); }
a / b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 2), 4);
}
#[test]
fn test_add_negative() {
assert_eq!(add(-1, 1), 0, "addition with negative");
}
#[test]
#[should_panic(expected = "divide by zero")]
fn test_divide_by_zero() {
divide(1, 0);
}
#[test]
#[ignore = "not implemented"]
fn test_future() { unimplemented!() }
}
Integration Tests
my-project/
├── Cargo.toml
├── src/lib.rs
└── tests/
├── common/ # Test shared modules
│ └── mod.rs
├── integration_test.rs
└── api_test.rs
// tests/integration_test.rs — Each file is an independent crate
use my_project::add;
#[test]
fn integration_test() {
assert_eq!(add(1, 2), 3);
}
// tests/common/mod.rs — Shared helper functions
pub fn setup() { /* ... */ }
Documentation Tests (doctest)
/// Add two numbers.
///
/// ```
/// use my_crate::add;
/// assert_eq!(add(2, 3), 5);
/// ```
///
/// ```rust,should_panic
/// my_crate::divide(1, 0);
/// ```
///
/// ```rust,no_run
/// // Compile but do not run
/// loop {}
/// ```
pub fn add(a: i32, b: i32) -> i32 { a + b }
cargo test Commands
cargo test # Run all tests
cargo test test_name # Filter by name
cargo test -- --nocapture # Show println output
cargo test -- --test-threads=1 # Single thread
cargo test -- --skip test_name # Skip specific tests
cargo test -- --ignored # Only run #[ignore] tests
cargo test -- --include-ignored # Include ignored tests
cargo test --doc # Run only documentation tests
cargo test -p my-crate # Specific package
Benchmarks
Stable projects should prioritize Criterion. The built-in libtest #[bench] still relies on nightly's #![feature(test)], which cannot be used as a stable default solution.
// Nightly-only builtin approach (do not use for stable gatekeeping)
#![feature(test)]
extern crate test;
#[cfg(test)]
mod benches {
use test::Bencher;
use super::*;
#[bench]
fn bench_add(b: &mut Bencher) {
b.iter(|| add(1, 2));
}
}
// Stable approach using criterion
// [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] }
use criterion::{black_box, Criterion};
fn bench_add(c: &mut Criterion) {
c.bench_function("add", |b| b.iter(|| add(black_box(1), black_box(2))));
}
criterion_group!(benches, bench_add);
criterion_main!(benches);
Code Coverage
# Install
cargo install cargo-llvm-cov
# Usage
cargo llvm-cov # Run and report results
cargo llvm-cov --open # Generate HTML reports
cargo llvm-cov --lcov --output-path lcov.info # LCOV format output
Workflow
- Prepare test environment — Ensure cargo test is available, confirm test types (unit/integration/documentation)
- Write unit tests — Implement #[test] functions within
#[cfg(test)] modules
- Add integration tests — Create independent crate-type files in the tests/ directory
- Add documentation tests — Embed executable code blocks inside /// comments
- Run and debug — Use cargo test; use --nocapture to locate issues; prioritize resource isolation or test grouping for shared resources, avoiding permanent serialization of full test suites
- Concurrency and platform validation — Implement bounded assertions for queue limits, slow consumers, disconnections, cancellations, timeouts, and graceful shutdowns on real target platforms; run platform-specific code directly
- Coverage checks — Use cargo llvm-cov to verify coverage ranges
Gotchas
- Code in
#[cfg(test)] modules does not compile into release builds — Helper functions should reside in tests/common/mod.rs
- Integration test files are independent crates — Cannot use super:: or crate:: prefixes within them
- Hidden lines (#) in documentation tests remain executable but invisible to the compiler
- cargo test runs by default in parallel; shared resource conflicts should be resolved via unique temporary directories/ports, process isolation, nextest test group limits, or local serialization only when necessary
- #[bench] requires #![feature(test)] — Stable Rust must use criterion instead
- Providing a broad timeout for async tests hides deadlock causes; also assert intermediate states, task recovery, and resource counts
On-Demand Resources
- Test examples
- Macro commands and command reference
- Concurrency, daemon, and platform testing: Test for memory leaks in tasks, slow consumers, race conditions, real processes, and resource-limited parallelism when reading.
examples/golden-tests/: CI compilation and execution of doctest golden examples
Official References
1---2name: rust-testing3description: Design, implement, and validate Rust tests, including unit, integration, doctest, compile-fail, property, fuzz, benchmark, coverage, async, concurrency, process, daemon, IPC, terminal, platform, and hardware-facing test strategies. Use when users ask for Rust test architecture, flaky-test diagnosis, coverage gates, benchmarks, trybuild, cargo-nextest, real-process tests, or failure-path verification.4---56# Rust Testing and Benchmarking78> Based on Chapter 11 of *The Rust Programming Language* and the Rust Book.910## Capability Boundaries1112### ✅ Strengths131. Unit tests (using `#[test]`, organizing test modules with `#[cfg(test)]`)142. Assertion macros (`assert!`, `assert_eq!`, `assert_ne!`, `debug_assert!`)153. Test attributes (`#[should_panic]`, `#[ignore]`, `#[cfg(test)]`)164. Integration tests (tests/ directory and shared modules)175. Documentation tests (code blocks, hidden lines with `#`, should_panic/no_run/ignore flags)186. cargo test runner (filtering, --nocapture, --test-threads, --include-ignored options)197. Stable Criterion benchmarks; explicitly distinguishing stable Criterion from nightly-only libtest `#[bench]` attribute208. Code coverage using cargo-llvm-cov219. Asynchronous race conditions, backpressure, timeouts, process/daemon models, platform matrices, and resource-constrained testing2223### ⚠️ Prerequisites241. Understanding of Rust module system (rust-workspace)2526### ❌ Out of Scope271. Property-based tests (`proptest`) → Not currently covered282. Mock objects → Not currently covered293. Basic Rust syntax → Use `rust-stable` skill instead3031## When to Use3233- "Write unit tests"34- "Where should integration tests be placed?"35- "How do I write documentation tests?"36- "Performance benchmarking"37- "Check code coverage"3839---4041## Routing Boundary4243Use `rust-java-migration-testing` when tests must disposition a source Java suite, distinguish mirrored tests from golden/live differential evidence, add target-specific ownership/async/error/component obligations, audit coverage-chasing tests, or verify migration lifecycle/adapter/host acceptance. Keep this skill focused on general Rust test mechanics and Rust-native test architecture.4445## Unit Tests4647```rust48// src/lib.rs49pub fn add(a: i32, b: i32) -> i32 { a + b }50pub fn divide(a: i32, b: i32) -> i32 {51 if b == 0 { panic!("divide by zero"); }52 a / b53}5455#[cfg(test)]56mod tests {57 use super::*;5859 #[test]60 fn test_add() {61 assert_eq!(add(2, 2), 4);62 }6364 #[test]65 fn test_add_negative() {66 assert_eq!(add(-1, 1), 0, "addition with negative");67 }6869 #[test]70 #[should_panic(expected = "divide by zero")]71 fn test_divide_by_zero() {72 divide(1, 0);73 }7475 #[test]76 #[ignore = "not implemented"]77 fn test_future() { unimplemented!() }78}79```8081## Integration Tests8283```text84my-project/85├── Cargo.toml86├── src/lib.rs87└── tests/88 ├── common/ # Test shared modules89 │ └── mod.rs90 ├── integration_test.rs91 └── api_test.rs92```9394```rust95// tests/integration_test.rs — Each file is an independent crate96use my_project::add;9798#[test]99fn integration_test() {100 assert_eq!(add(1, 2), 3);101}102103// tests/common/mod.rs — Shared helper functions104pub fn setup() { /* ... */ }105```106107## Documentation Tests (doctest)108109```rust110/// Add two numbers.111///112/// ```113/// use my_crate::add;114/// assert_eq!(add(2, 3), 5);115/// ```116///117/// ```rust,should_panic118/// my_crate::divide(1, 0);119/// ```120///121/// ```rust,no_run122/// // Compile but do not run123/// loop {}124/// ```125pub fn add(a: i32, b: i32) -> i32 { a + b }126```127128## cargo test Commands129130```bash131cargo test # Run all tests132cargo test test_name # Filter by name133cargo test -- --nocapture # Show println output134cargo test -- --test-threads=1 # Single thread135cargo test -- --skip test_name # Skip specific tests136cargo test -- --ignored # Only run #[ignore] tests137cargo test -- --include-ignored # Include ignored tests138cargo test --doc # Run only documentation tests139cargo test -p my-crate # Specific package140```141142## Benchmarks143144Stable projects should prioritize Criterion. The built-in libtest `#[bench]` still relies on nightly's `#![feature(test)]`, which cannot be used as a stable default solution.145146```rust147// Nightly-only builtin approach (do not use for stable gatekeeping)148#![feature(test)]149extern crate test;150151#[cfg(test)]152mod benches {153 use test::Bencher;154 use super::*;155156 #[bench]157 fn bench_add(b: &mut Bencher) {158 b.iter(|| add(1, 2));159 }160}161162// Stable approach using criterion163// [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] }164use criterion::{black_box, Criterion};165166fn bench_add(c: &mut Criterion) {167 c.bench_function("add", |b| b.iter(|| add(black_box(1), black_box(2))));168}169criterion_group!(benches, bench_add);170criterion_main!(benches);171```172173## Code Coverage174175```bash176# Install177cargo install cargo-llvm-cov178179# Usage180cargo llvm-cov # Run and report results181cargo llvm-cov --open # Generate HTML reports182cargo llvm-cov --lcov --output-path lcov.info # LCOV format output183```184185## Workflow1861871. Prepare test environment — Ensure cargo test is available, confirm test types (unit/integration/documentation)1882. Write unit tests — Implement #[test] functions within `#[cfg(test)]` modules1893. Add integration tests — Create independent crate-type files in the tests/ directory1904. Add documentation tests — Embed executable code blocks inside /// comments1915. Run and debug — Use cargo test; use --nocapture to locate issues; prioritize resource isolation or test grouping for shared resources, avoiding permanent serialization of full test suites1926. Concurrency and platform validation — Implement bounded assertions for queue limits, slow consumers, disconnections, cancellations, timeouts, and graceful shutdowns on real target platforms; run platform-specific code directly1937. Coverage checks — Use cargo llvm-cov to verify coverage ranges194195## Gotchas1961971. Code in `#[cfg(test)]` modules does not compile into release builds — Helper functions should reside in tests/common/mod.rs1982. Integration test files are independent crates — Cannot use super:: or crate:: prefixes within them1993. Hidden lines (#) in documentation tests remain executable but invisible to the compiler2004. cargo test runs by default in parallel; shared resource conflicts should be resolved via unique temporary directories/ports, process isolation, nextest test group limits, or local serialization only when necessary2015. #[bench] requires #![feature(test)] — Stable Rust must use criterion instead2026. Providing a broad timeout for async tests hides deadlock causes; also assert intermediate states, task recovery, and resource counts203204## On-Demand Resources205206- [Test examples](examples/examples.md)207- [Macro commands and command reference](references/references.md)208- [Concurrency, daemon, and platform testing](references/concurrency-daemon-platform-testing.md): Test for memory leaks in tasks, slow consumers, race conditions, real processes, and resource-limited parallelism when reading.209- `examples/golden-tests/`: CI compilation and execution of doctest golden examples210211## Official References212213- [The Book ch 11](https://doc.rust-lang.org/book/ch11-00-testing.html)214- [Rustdoc Book — doctest documentation](https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html)215- [cargo test command reference](https://doc.rust-lang.org/cargo/commands/cargo-test.html)216- [criterion.rs library docs](https://docs.rs/criterion/)