nextest

star 0

Use when running Rust tests with cargo-nextest. Keywords: cargo nextest, nextest, test runner, run tests, test parallelism, test threads, flaky test, retry, slow test, timeout, test group, filterset, test partition, sharding, continuous integration (CI) testing, nextest profile, nextest config, .config/nextest.toml, cargo nextest run, cargo nextest list, test archive, JUnit, stress test, miri, test coverage, cargo-mutants, criterion, debugger, tracer

leynos By leynos schedule Updated 2/25/2026

name: nextest description: "Use when running Rust tests with cargo-nextest. Keywords: cargo nextest, nextest, test runner, run tests, test parallelism, test threads, flaky test, retry, slow test, timeout, test group, filterset, test partition, sharding, continuous integration (CI) testing, nextest profile, nextest config, .config/nextest.toml, cargo nextest run, cargo nextest list, test archive, JUnit, stress test, miri, test coverage, cargo-mutants, criterion, debugger, tracer" globs: ["/Cargo.toml", "/.config/nextest.toml"] user-invocable: false

cargo-nextest: The Rust Test Runner

cargo-nextest runs each test in its own process, in parallel. It builds test binaries, queries them for tests, then executes each test individually. This eliminates bottlenecks from long-pole tests and provides structured per-test results. Custom test harnesses may need adaptation for this model.

Quick Reference

Running Tests

cargo nextest run                          # Run all tests
cargo nextest run test_name                # Run tests matching substring
cargo nextest run -p my-crate              # Run tests in a specific package
cargo nextest run --workspace              # Run all workspace tests
cargo nextest run -j 4                     # Limit to 4 concurrent tests
cargo nextest run -j 1                     # Run tests serially
cargo nextest run --no-fail-fast           # Don't stop on first failure
cargo nextest run --retries 2              # Retry failures up to 2 times
cargo nextest run --no-capture             # Show output live (serial)
cargo nextest run -P ci                    # Use the "ci" profile

Listing and Selecting Tests

cargo nextest list                         # List all tests
cargo nextest list -T json-pretty          # List in JSON format
cargo nextest run -E 'package(my-crate)'   # Filterset: by package
cargo nextest run -E 'test(/regex/)'       # Filterset: by regex
cargo nextest run -E 'deps(my-crate)'      # Filterset: crate + dependencies
cargo nextest run -- --skip slow --exact test_foo  # Skip/exact matching

CI Essentials

cargo nextest run -P ci --no-fail-fast     # CI profile, run all tests
cargo nextest run --partition slice:1/3    # Shard across 3 runners
cargo nextest archive --archive-file a.tar.zst  # Archive for reuse
cargo nextest run --archive-file a.tar.zst      # Run from archive

Parallelism and Thread Control

By default, nextest runs num-cpus tests concurrently. Control this with:

  • CLI: -j N or --test-threads N (also num-cpus)
  • Env: NEXTEST_TEST_THREADS=N
  • Config: test-threads = N or test-threads = "num-cpus"

Negative values are relative to CPU count (e.g., -2 means num_cpus - 2).

Heavy tests (threads-required)

Mark resource-intensive tests to consume multiple thread slots:

# .config/nextest.toml
[[profile.default.overrides]]
filter = 'test(/^tests::heavy::/)'
threads-required = 2        # Each takes 2 of the thread budget

# Special values:
# threads-required = "num-cpus"          — effectively serial
# threads-required = "num-test-threads"  — monopolise all slots

Test groups (mutual exclusion / rate-limiting)

Logical semaphores/mutexes for subsets of tests:

[test-groups]
serial-db = { max-threads = 1 }      # Mutex
rate-limited = { max-threads = 4 }    # Semaphore with 4 permits

[[profile.default.overrides]]
filter = 'package(db-tests)'
test-group = 'serial-db'

[[profile.default.overrides]]
filter = 'test(api_)'
test-group = 'rate-limited'

Inspect with cargo nextest show-config test-groups.


Configuration

Config file: .config/nextest.toml in the workspace root.

Profiles

Profiles provide named sets of options. Select with -P <name> or NEXTEST_PROFILE=<name>.

[profile.ci]
fail-fast = false
retries = 2

For complete profile examples (including profile.default and profile.ci.junit for JUnit output), see ref/ci-patterns.md.

Profiles inherit from default unless inherits is specified.

Hierarchical resolution

  1. CLI arguments
  2. Environment variables
  3. Per-test overrides (in profile, then default)
  4. Profile configuration
  5. Default configuration

Per-test overrides

[[profile.default.overrides]]
filter = 'test(test_e2e)'
retries = 3
slow-timeout = { period = "120s", terminate-after = 5 }
threads-required = 2

Overrides support: retries, slow-timeout, leak-timeout, threads-required, test-group, success-output, failure-output, priority, run-extra-args, default-filter.


Retries and Flaky Tests

retries = 3                                              # Simple
retries = { backoff = "fixed", count = 3, delay = "1s" } # Fixed delay
retries = { backoff = "exponential", count = 4, delay = "2s", max-delay = "10s", jitter = true }

CLI --retries N and env NEXTEST_RETRIES=N override all config including per-test overrides, and disable backoff delays.

A test that fails then succeeds on retry is marked flaky (ultimately passes, exit code 0).


Timeouts

Slow test warnings

slow-timeout = "60s"             # Warn after 60s

Terminating hung tests

slow-timeout = { period = "30s", terminate-after = 4 }
# Warns at 30s, terminates at 120s (4 x 30s)
# Grace period before SIGKILL: default 10s
slow-timeout = { period = "30s", terminate-after = 4, grace-period = "5s" }

Global timeout

global-timeout = "2h"      # Entire test run must finish in 2 hours

Timeout as success (fuzz tests)

[[profile.default.overrides]]
filter = 'package(fuzz-targets)'
slow-timeout = { period = "30s", terminate-after = 1, on-timeout = "pass" }

Filtersets (Domain-specific language, DSL)

Specified with -E / --filterset. Core concepts:

  • Predicates: test(...), package(...), deps(...), rdeps(...), kind(...), platform(...), default()
  • Matchers: exact (=), contains (~), regex (/../), glob (#)
  • Operators: not/!, and/&/-, or/|/+ with () grouping
  • CLI filtersets intersect with default-filter unless --ignore-default-filter is set

See ref/filterset-dsl.md for the complete predicate table, matcher rules, operator precedence, escape sequences, and advanced examples.


Environment-Aware Configuration

Per-platform overrides

[[profile.default.overrides]]
platform = 'cfg(target_os = "linux")'
slow-timeout = "120s"

[[profile.default.overrides]]
platform = { host = "cfg(unix)", target = "aarch64-apple-darwin" }
threads-required = 2

Agent sandbox considerations

When running inside an agent sandbox (constrained CPU/memory):

  • Use -j 2 or -j 4 to limit parallelism
  • Set generous timeouts: slow-timeout = { period = "120s", terminate-after = 3 }
  • Consider --no-fail-fast to gather all failures in one run
  • Use --show-progress=counter for non-interactive output
  • Set NEXTEST_NO_INPUT_HANDLER=1 to disable terminal key handling

Continuous integration (CI) workflow overview

For production-ready profile config, sharding strategies, build/run split archives, and full GitHub/GitLab examples, see ref/ci-patterns.md.

cargo nextest run -P ci
cargo nextest run -P ci --partition slice:1/3
cargo nextest archive --workspace --archive-file tests.tar.zst
cargo nextest run --archive-file tests.tar.zst --partition slice:1/3

Archiving and Reusing Builds

Build once and run on multiple machines (same platform and source revision):

cargo nextest archive --archive-file tests.tar.zst
cargo nextest run --archive-file tests.tar.zst

Use --workspace-remap <path> when the checkout path differs on the target machine.

See ref/ci-patterns.md for full archive patterns, include/exclude options, and CI artifact workflows.


Stress Testing

cargo nextest run --stress-count 10 test_flaky    # Run 10 times each
cargo nextest run --stress-count infinite test_x   # Run indefinitely
cargo nextest run --stress-duration 5m test_x      # Run for 5 minutes

Record, Replay, and Rerun (Experimental)

Enable in ~/.config/nextest/config.toml:

[experimental]
record = true

[record]
enabled = true

Then:

cargo nextest run                   # Automatically recorded
cargo nextest run -R latest         # Rerun only failures
cargo nextest replay                # Replay last run's output
cargo nextest store list            # List recorded runs
cargo nextest store export latest   # Export portable recording

Test Priorities

[[profile.default.overrides]]
filter = 'test(smoke_)'
priority = 50          # Run early (range: -100 to 100, default: 0)

[[profile.default.overrides]]
filter = 'test(slow_e2e_)'
priority = -50         # Run late

Reporter and Output Control

Option Values Default
--failure-output immediate, final, immediate-final, never immediate
--success-output immediate, final, immediate-final, never never
--status-level none, fail, retry, slow, leak, pass, skip, all pass
--final-status-level none, fail, flaky, slow, skip, pass, all flaky
--show-progress auto, none, bar, counter, only auto

Press t during a run to dump status of currently-running tests (interactive terminals only). On macOS, Ctrl-T also works. On any Unix, send SIGUSR1.


Setup Scripts (Experimental)

Pre-test scripts that can set environment variables for tests:

experimental = ["setup-scripts"]

[scripts.setup.db-seed]
command = 'cargo run -p seed-db'
slow-timeout = { period = "60s", terminate-after = 2 }

[[profile.default.scripts]]
filter = 'rdeps(db-tests)'
setup = 'db-seed'

Scripts write env vars to $NEXTEST_ENV:

echo "DATABASE_URL=postgres://localhost/test" >> "$NEXTEST_ENV"

Key Environment Variables

Nextest reads

Variable Purpose
NEXTEST_TEST_THREADS Override test thread count
NEXTEST_RETRIES Override retry count
NEXTEST_PROFILE Select profile
NEXTEST_FAILURE_OUTPUT Override failure output mode
NEXTEST_VERBOSE Verbose output

Nextest sets (at test runtime)

Variable Value
NEXTEST Always "1"
NEXTEST_RUN_ID UUID for the run
NEXTEST_EXECUTION_MODE "process-per-test"
NEXTEST_ATTEMPT 1-indexed attempt number
NEXTEST_TEST_GROUP Group name or "@global"
NEXTEST_BIN_EXE_<name> Path to binary target (integration tests)
NEXTEST_BINARY_ID Binary ID of the current test
NEXTEST_ATTEMPT_ID Globally unique attempt identifier
NEXTEST_TEST_GLOBAL_SLOT Global slot number (0-indexed, unique among running tests)
NEXTEST_TEST_GROUP_SLOT Group slot number ("none" if not in a group)

Slot numbers are useful for assigning resources like port numbers to tests. They are unique for the lifetime of the test, stable across retries, and compact (each test gets the smallest available slot).

Environment safety

Because nextest runs each test in its own process, calling std::env::set_var at the beginning of a test is safe in practice (before spawning threads).


Debugger and Tracer Support

cargo nextest run --debugger "rust-gdb --args" test_name
cargo nextest run --debugger "rust-lldb --" test_name
cargo nextest run --tracer strace test_name
cargo nextest run --tracer "strace -f" test_name     # Follow child processes

Both modes disable timeouts and output capture, and require exactly one test to be selected. Key differences:

  • --debugger: Passes stdin through, disables signal handling and process groups (interactive debugging with gdb, lldb, WinDbg, CodeLLDB)
  • --tracer: Null stdin, standard signal handling, process groups for isolation (non-interactive tracing with strace, dtruss, truss)

Integrations

Miri (undefined behaviour detection)

cargo miri nextest run
cargo miri nextest run --target mips64-unknown-linux-gnuabi64  # Cross-interpretation

Nextest auto-selects the default-miri profile under Miri. Configure it in .config/nextest.toml:

[profile.default-miri]
slow-timeout = { period = "60s", terminate-after = 2 }

Miri tests run in parallel with nextest (Miri itself is single-threaded, so cargo miri test is limited to serial execution). Archiving is not supported under Miri.

Test coverage

cargo llvm-cov nextest

Merge with doctests (nextest doesn't run doctests):

cargo llvm-cov --no-report nextest
cargo llvm-cov --no-report --doc
cargo llvm-cov report --doctests --lcov --output-path lcov.info

Mutation testing (cargo-mutants)

cargo mutants --test-tool=nextest

Or set permanently in .cargo/mutants.toml:

test_tool = "nextest"

Criterion benchmarks in test mode

By default, cargo nextest run excludes benchmarks. To verify benchmarks compile and don't panic (single iteration, no measurement):

cargo nextest run --all-targets   # Include benchmarks
cargo nextest run --benches       # Only benchmarks

Requires Criterion 0.5.0+. For actual performance measurement, use the experimental cargo nextest bench (requires experimental = ["benchmarks"]).


Reference

For detailed reference on specific topics, see the ref/ directory in this skill:

  • ref/config-reference.md — Full configuration parameter reference
  • ref/filterset-dsl.md — Complete filterset DSL reference
  • ref/ci-patterns.md — CI/CD patterns for archiving, sharding, and GitHub Actions
Install via CLI
npx skills add https://github.com/leynos/nextest-skill --skill nextest
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator