Install
$ agentstack add skill-xobotyi-cc-foundry-rust-testing ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Rust Testing
Prefer real implementations and hand-written fakes over heavy mocking — a test wired to a mock asserts how the code calls its collaborators, not what the code does. Tests are documentation that runs: a reader should learn the intended behavior from the test name, the arrange/act/assert shape, and the doctest examples on public items. Test the public contract, not the private call graph.
Route to Reference
- Test layout depth —
${CLAUDE_SKILL_DIR}/references/layout.mdUnit vs integration vs doctest mechanics,tests/
crate-per-file model, tests/common/mod.rs shared-helper pattern, binary-crate lib.rs split, doctest attributes (no_run, should_panic, ignore, hidden # lines), assert_matches! and pretty_assertions usage
- Property and snapshot testing, benchmarks —
${CLAUDE_SKILL_DIR}/references/property-and-snapshot.mdproptest vs
quickcheck strategy authoring and shrinking, insta inline/file snapshots and cargo insta review, criterion harness setup and std::hint::black_box
- Mocking and fixtures —
${CLAUDE_SKILL_DIR}/references/mocking-and-fixtures.mdmockall#[automock]vsmock!,
predicates and call counts, the trait-seam pattern for in-memory fakes, rstest #[case]/#[fixture] params, serialtest, assertcmd + predicates for CLI integration
- Async tests —
${CLAUDE_SKILL_DIR}/references/async-tests.md#[tokio::test]flavors,tokio::time::pause/
advance for deterministic timers, tokio-test macros; cross-references the rust skill async reference for runtime semantics
- Pinned crate versions —
${CLAUDE_SKILL_DIR}/references/versions.mddate-stamped dev-dependency versions for the
testing ecosystem crates ("as of 2026-06")
Read the relevant reference before writing non-trivial tests in that area.
Test Layout
- Place unit tests in a
#[cfg(test)] mod testsblock in the same file as the code under test; bring the parent into
scope with use super::*. #[cfg(test)] keeps test code (and its helpers) out of the build artifact.
- Use unit tests to exercise private items — Rust's privacy rules let a child
testsmodule reach its ancestors, so
testing internals is possible. Reserve this for genuinely tricky private logic; prefer driving private code through the public API.
- Place integration tests in the top-level
tests/directory, next tosrc/. Cargo compiles each file as its own
crate, so they reach only the public API via use my_crate::... — exactly as an external consumer would.
- Share integration-test helpers from
tests/common/mod.rs, nottests/common.rs. Themod.rsform is not compiled as
a separate test crate, so it produces no spurious empty "running 0 tests" section. Import it with mod common;.
- For a binary crate, keep
main.rsa thin shell that calls intolib.rs; integration tests then exercise the library
crate, since only library crates expose items to external use.
- Write doctests as
///examples on public items. They double as compiled documentation and run under
cargo test --doc. Hide setup lines with a leading # and use `no_run for examples that must compile but not run.
Assertions
- Use
assert!for booleans andassert_eq!/assert_ne!for value equality; add a trailing message argument when the
failure is not self-explanatory.
- Use
assert_matches!to assert a value matches a pattern (including enum variants with bindings) — it is stable in
std as of Rust 1.96 and requires use std::assert_matches::assert_matches;.
- Add
pretty_assertionsas a dev-dependency anduse pretty_assertions::{assert_eq, assert_ne};in test modules when
comparing large structs or collections — it renders colored line-by-line diffs instead of one unreadable line.
Runner
- Run unit and integration tests with
cargo nextest run— it builds each test binary, queries it for tests, and runs
every test in its own process in parallel, giving clean per-test isolation and output.
- Always pair it with
cargo test --doc. Nextest does not run doctests, so a project that only runs nextest silently
skips every doctest. Both commands belong in the verification gate.
- Scope runs with the same selectors as cargo:
-p/--workspace,--lib/--test,-Efor
expression filtering, and --no-capture to see test stdout/stderr.
- Drive flaky-test triage with
--retriesand stop early with--fail-fast; prefer fixing nondeterminism over
leaving retries on permanently.
Mocking Decision Rule
Default to the cheapest test double that still proves the behavior, escalating only when the boundary forces it:
- Real implementation — call the actual code. The default for pure logic and anything with no external dependency.
- In-memory fake — a real working implementation backed by memory (e.g. a
HashMap-backed store behind a repository
trait). Preferred for stateful collaborators; the fake is reusable across many tests.
- Hand-written double — a small struct implementing the collaborator's trait with canned answers. Use when a fake is
overkill and you need one or two stubbed responses.
- mockall — generate a mock with
#[automock](ormock!for foreign/multi-trait types) only at IO or
nondeterministic boundaries: network, filesystem, clock, randomness, external services behind a trait seam.
Define collaborators behind a trait so the production type and the test double are interchangeable. Assert on observable outcomes — return values, resulting state, emitted effects — not on which methods were called in which order. Do not use mockall Sequence/in_sequence to lock internal call order; ordering assertions make tests break on harmless refactors. Argument matchers and call counts are acceptable when the call itself is the contract (e.g. "charge the card exactly once").
Property, Snapshot, and Benchmarks
- Reach for property testing when a function must hold an invariant across a large input space (round-trips,
idempotence, ordering). Use proptest (generates and shrinks values from declarative strategies) or quickcheck (derives generators from Arbitrary); proptest is the richer default for custom input domains.
- Use
instafor snapshot testing of large or structural output (serialized data, rendered text, debug dumps). Review
and accept changes with cargo insta review; commit the approved .snap files. Snapshots make intent diffable rather than hand-asserting every field.
- Put benchmarks in
benches/withcriterion, setharness = falseon the[[bench]]target in Cargo.toml, and wrap
benchmarked inputs/outputs in std::hint::black_box so the optimizer cannot fold the work away. Benchmarks measure; they are not correctness tests.
Async Tests
- Annotate async tests with
#[tokio::test]. Default to the current-thread flavor; use
#[tokio::test(flavor = "multi_thread", worker_threads = N)] only when the test genuinely needs real parallelism.
- Make time deterministic with
tokio::time::pause()andtokio::time::advance(duration)instead of realsleep.
Advancing virtual time fires timers instantly and removes wall-clock flakiness from timeout and interval tests.
- Mock async-trait collaborators with mockall (compatible with
async_trait/trait_variant), returning futures via
.returning(|| Box::pin(async { ... })). The mocking decision rule applies unchanged — mock only at IO boundaries.
Application
When writing tests:
- Apply these conventions silently; do not narrate each rule. Name tests for the behavior under test, not the method.
- Match the surrounding project: if it already standardizes on
cargo test, rstest fixtures, or a specific assertion
crate, follow that and flag any divergence once rather than rewriting wholesale.
- Add the verification gate commands you rely on (
cargo nextest run,cargo test --doc) when introducing tests to a
project that lacks them.
When reviewing tests:
- Flag mocks at non-IO boundaries, assertions on internal call order, and doctests that would be skipped by a
nextest-only gate. Cite the specific test and show the corrected form inline.
- Flag nondeterminism (real sleeps, wall-clock reads, unseeded randomness, shared global state without
serial_test) as
a flaky-test risk and propose the deterministic alternative.
Integration
The rust skill governs language conventions (ownership, error handling, module layout, the cargo/clippy/rustfmt gates); this skill governs test structure and the testing ecosystem. When both apply, defer to rust for how the code under test is written and to this skill for how it is exercised. For async runtime semantics behind #[tokio::test] (executor model, Send bounds, cancellation), consult the rust skill's async reference rather than restating it here.
Quality Checks
- Unit tests live in
#[cfg(test)] mod tests; integration tests intests/touch only the public API; shared helpers
in tests/common/mod.rs.
- Every behavior has a clear arrange/act/assert shape and a name that reads as documentation.
- The verification gate runs both
cargo nextest runandcargo test --doc— doctests are never silently skipped. - Mocking escalates real -> in-memory fake -> hand-written double -> mockall, and stops at the lowest rung that proves
the behavior; no assertions on internal call order.
- Async and time-dependent tests are deterministic (paused virtual time, seeded randomness, no real sleeps).
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: xobotyi
- Source: xobotyi/cc-foundry
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.