--- name: cargo-test-driven-development description: The `cargo nextest run` workflow for Rust TDD — fast, parallel, isolates flakes. Falls back to `cargo test` when nextest isn't installed. when_to_use: You're the tester role, or coder role in the RED phase of TDD, on a Rust codebase. tags: [rust, testing, tdd, versioned] --- # Cargo TDD workflow ## Runner **Primary: `cargo nextest run` (0.9.140 as of 2026-07).** Why over `cargo test`: - Parallel process isolation — one panicking test doesn't taint others. - Structured, greppable output. - `--retries N` policy for flake-tolerance without hiding real failures. - Per-test timeouts. Install: `cargo install cargo-nextest --locked` in the runtime image; CI runs `cargo nextest run --profile ci`. ## The RED-GREEN loop (Rust flavor) 1. **RED — write the failing test.** ```rust #[tokio::test] async fn workspace_scoped_get_rejects_cross_workspace() { let pool = testkit::pool().await; let mine = seed_workspace(&pool).await; let theirs = seed_workspace(&pool).await; let row = insert_row(&pool, theirs).await; assert!(get(&pool, row.id, mine).await.unwrap().is_none()); } ``` Run: `cargo nextest run workspace_scoped_get_rejects_cross_workspace`. Confirm the test fails because the fn doesn't exist yet or because behavior is missing. 2. **GREEN — minimal implementation.** Hardcode if that's what the ONE test asks for. Trust that the NEXT test will force you to generalize. 3. **REFACTOR under green.** Extract, rename, tighten. Re-run after each keystroke that could break something. `cargo nextest run ` on the specific test is faster than the whole suite. ## Speed tricks - **`cargo nextest run -j 8`** — parallelize across cores. - **`cargo nextest run --no-fail-fast`** — see all failures in one pass, not just the first. - **`cargo nextest run -E 'test(foo)'`** — filter expression language, more powerful than a substring. - **`cargo check --tests`** before running — catches type errors ~10× faster than compiling test binaries. ## Coverage - **`cargo llvm-cov nextest`** — nextest under llvm-cov instrumentation. Reports per-file line coverage. - CI enforces ≥90% on changed lines. Locally: `cargo llvm-cov nextest --html && open target/llvm-cov/html/index.html`. ## Integration tests that hit real infra - Postgres via `testcontainers` — spins a fresh container per test suite. NEVER mock the DB when the test's job is to prove a query. - HTTP via `axum::Router` + `tower::ServiceExt::oneshot` — driver is in-process, no port needed. - Docker via `bollard` mock or a `#[ignore]` guarded live test. ## Anti-patterns - **Snapshot-only test files.** A snapshot proves nothing structural. Pair with real assertions. - **`#[ignore]`d tests that stay ignored.** Ignore is a debt marker — file the ticket to fix and put the ticket ID in the ignore reason. - **Testing panics with `should_panic` when a `Result::Err` would do.** Panics on the happy path of a test framework are user-hostile output.