cargo-test
Configures and runs Rust's built-in `cargo test` - `#[test]` + `#[should_panic]` + `Result<(), E>` returns; integration tests in `tests/`; doc-tests embedded in `///` comments; `--lib` / `--bins` / `--all-targets` / `--workspace` selection; `cargo bench` (nightly) + Criterion (stable); `cargo test -- --test-threads=1` for serial; `cargo test -- --nocapture` to see println output. Use for any Rust project - testing is built into Cargo, no install needed.
Install with skills.sh (any agent)
npx skills add testland/qa --skill cargo-testcargo-test
Overview
Per doc.rust-lang.org/book/ch11-00-testing.html (opens in new window):
Rust's testing is built into Cargo. The #[test] attribute marks test functions; cargo test discovers and runs them.
Three test categories per the Rust Book (opens in new window):
| Category | Location | Purpose |
|---|---|---|
| Unit tests | Same file as code, in #[cfg(test)] mod tests { ... } | Test private + internal logic |
| Integration tests | tests/ directory at crate root | Test public API as external user |
| Doc tests | Inside /// doc comments | Verify documentation examples |
How to use
Step 1 - Unit tests
// src/math.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn adds_two_numbers() {
assert_eq!(add(1, 2), 3);
}
#[test]
fn handles_zero() {
assert_eq!(add(0, 0), 0);
}
}Run:
cargo test # all tests
cargo test add # filter by name pattern
cargo test --lib # only unit tests in lib
cargo test --bins # binary tests
cargo test --all-targets # everything
cargo test --workspace # multi-crate workspaceStep 2 - Assertion macros
assert!(condition);
assert!(condition, "format message: {}", value);
assert_eq!(actual, expected);
assert_eq!(actual, expected, "with message");
assert_ne!(actual, unexpected);
// Custom error message:
assert!(actual > 0, "Expected positive, got {}", actual);For richer assertions, the pretty_assertions crate provides diff-rich macros:
use pretty_assertions::assert_eq;
assert_eq!(actual_complex_struct, expected_complex_struct);
// shows colorized diff on failureStep 3 - #[should_panic]
#[test]
#[should_panic]
fn panics_on_negative() {
sqrt(-1.0);
}
#[test]
#[should_panic(expected = "negative")]
fn specific_panic_message() {
sqrt(-1.0);
// panics with message containing "negative"
}Step 4 - Result return for ?-style propagation
#[test]
fn parses_config() -> Result<(), Box<dyn Error>> {
let cfg = Config::from_file("test/fixtures/config.toml")?;
assert_eq!(cfg.port, 8080);
Ok(())
}Test passes if Ok(()); fails if Err(...). Avoids unwrap() boilerplate; allows ? operator in test bodies.
Step 5 - Integration tests
my-crate/
src/
lib.rs
tests/
integration_test.rs # automatically discovered
common/
mod.rs # shared helpers (note: NO mod.rs in tests/ root)// tests/integration_test.rs
use my_crate::Calculator;
#[test]
fn end_to_end() {
let c = Calculator::new();
assert_eq!(c.add(1, 2), 3);
}
mod common; // shared test helpers from tests/common/mod.rsEach file in tests/ compiles to its own binary - slower but better-isolated than unit tests.
Step 6 - Doc tests
/// Adds two numbers.
///
/// # Examples
///
/// ```
/// use my_crate::math::add;
/// assert_eq!(add(1, 2), 3);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}cargo test --doc runs only doc tests; cargo test runs everything including docs. The example IS the test - drift between docs + implementation is caught.
# Examples heading is convention; required for cargo doc HTML rendering to show the example.
Step 7 - Common test runner flags
cargo test -- --test-threads=1 # serial (single-threaded)
cargo test -- --nocapture # show stdout (println in tests)
cargo test -- --show-output # show stdout for passed tests too
cargo test -- --ignored # only run #[ignore]-marked tests
cargo test -- --include-ignored # run both ignored + normal
cargo test -- --list # list tests without running
cargo test some_pattern -- --exact # exact match (no substring)Step 8 - #[ignore] for slow tests
#[test]
#[ignore = "Requires network access"]
fn integration_with_external_api() {
// ...
}Run via cargo test -- --include-ignored.
Step 9 - Coverage and benchmarks
Both need an extra crate installed (cargo-llvm-cov / cargo-tarpaulin for coverage; Criterion for benchmarks on stable). Full commands, the Cargo.toml bench wiring, and the Criterion example are in references/coverage-and-benchmarks.md.
Step 10 - CI integration
- run: cargo test --all-targets --workspace
- run: cargo test --doc # explicit doc tests
- run: cargo install cargo-llvm-cov
- run: cargo llvm-cov --lcov --output-path coverage.lcov
- uses: codecov/codecov-action@v4
with: { files: coverage.lcov }Worked example
A crate exposes add(a, b) in src/math.rs and you need it tested before release.
Result: unit, doc, and coverage checks are all green - the function is release-ready.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Skip --all-targets | Doc tests + bench + examples not run | Always --all-targets (Step 1) |
Use unwrap() in test bodies | Test failure message is "called unwrap on None"; useless | Use Result<(), E> return + ? (Step 4) |
#[ignore] without reason | Forgotten ignored tests | Always include = "reason" (Step 8) |
assert!(x == y) instead of assert_eq! | Loses diff in failure | Use assert_eq! (Step 2) |
| Skip Criterion for performance work | Stdlib #[bench] is nightly-only; CI breaks | Use Criterion on stable (see references/coverage-and-benchmarks.md) |
Limitations
References
Coverage and benchmarks
View source (opens in new window)Coverage and benchmarks
Measurement tooling for cargo test. Both need an extra crate installed; neither ships in the stdlib test harness.
Coverage
Stable Rust support via cargo-tarpaulin (Linux) or cargo-llvm-cov (cross-platform):
# llvm-cov (recommended for cross-platform)
cargo install cargo-llvm-cov
cargo llvm-cov --html # HTML report
cargo llvm-cov --lcov --output-path coverage.lcov
cargo llvm-cov --fail-under-lines 80 # gate at 80%
# Or tarpaulin (Linux-only)
cargo install cargo-tarpaulin
cargo tarpaulin --out html --output-dir coverage/Benchmarks
Stable: use Criterion (mature, ergonomic):
cargo install cargo-criterion# Cargo.toml
[dev-dependencies]
criterion = "0.5"
[[bench]]
name = "math_bench"
harness = false// benches/math_bench.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use my_crate::math::add;
fn bench_add(c: &mut Criterion) {
c.bench_function("add 1 2", |b| b.iter(|| add(black_box(1), black_box(2))));
}
criterion_group!(benches, bench_add);
criterion_main!(benches);cargo bench
# Or via criterion CLI for richer reports:
cargo criterionNightly Rust has built-in #[bench] (cargo +nightly bench); not recommended for CI (requires nightly toolchain).
Sources
Related skills
ginkgo-tests
Configures and runs Ginkgo - Go BDD test framework with `Describe` / `Context` / `It` nesting; `BeforeEach` / `AfterEach` / `JustBeforeEach` / `JustAfterEach` lifecycle; Gomega matchers DSL (`Expect(actual).To(Equal(expected))`); parallel execution via `-p`; focus (`F` prefix) + skip (`P` prefix); `DescribeTable` + `Entry` for parametrized tests; `ginkgo` CLI tool. Use when working with Go on a BDD-style test suite (Kubernetes-ecosystem standard).
go-rust-mocking
Isolates dependencies in Go and Rust unit tests using test-double generation - Go: `go.uber.org/mock` (gomock + mockgen codegen) and `github.com/stretchr/testify/mock` (hand-written stubs); Rust: `mockall` crate with `#[automock]` for traits and `mock!` macro for structs and external traits. Use when a unit test reaches a database, HTTP client, file system, or any interface boundary that must be replaced with a controlled fake to keep tests fast, deterministic, and isolated.
go-test
Configures and runs Go's stdlib `testing` package - `func TestXxx(t *testing.T)` convention; table-driven tests via slice + range; `t.Run` for subtests with hierarchical names; `t.Parallel()` for parallel execution; benchmarks (`func BenchmarkXxx(b *testing.B)`); examples (`func ExampleXxx()`); fuzzing (`func FuzzXxx(f *testing.F)`); coverage via `go test -cover`/`-coverprofile`; build tags for selective compilation. Use for any Go project - testing is a stdlib feature, no install needed.
rstest-tests
Configures and runs rstest - Rust parametrized + fixture-based testing crate; `#[rstest]` attribute + `#[case(...)]` for parametrize; `#[fixture]` for reusable test setup; matrix tests via multiple `#[case]` × N (cartesian product); async test support via `#[async_std::test]` / `#[tokio::test]` + `#[rstest]`; `#[future]` for async fixtures. Use when working with Rust and needing parametrize/fixture patterns beyond stdlib `#[test]`.