cargo-nextest: Faster Rust Test Runner with Better Output
cargo-nextest is a next-generation test runner for Rust that replaces cargo test with a process-per-test execution model, better terminal output, built-in retry logic, and significantly faster CI runs. It's a drop-in replacement — your existing tests work unchanged.
Why nextest?
cargo test runs tests in threads within a single process. This is fast but has drawbacks:
- A test that leaks global state can corrupt later tests
- Output from multiple tests interleaves in the terminal
- No built-in retry for flaky tests
- No per-test timeout enforcement
nextest runs each test in its own process. This costs some startup overhead but delivers:
- Faster CI — nextest parallelizes more aggressively; typical speedup is 1.5-3× on large test suites
- Clean isolation — process-per-test prevents state leaks between tests
- Better output — live status per test, failure summary at the end
- Retry logic — automatically retry flaky tests before declaring them failures
- Per-test timeouts — kill tests that hang
Installation
cargo install cargo-nextest --lockedOr use the pre-built binary from the nextest releases page for faster CI setup.
In CI, use the official install action:
- uses: taiki-e/install-action@nextestBasic Usage
Drop-in replacement for cargo test:
cargo nextest run # replaces: cargo test
cargo nextest run --lib <span class="hljs-comment"># lib tests only
cargo nextest run --<span class="hljs-built_in">test integration_test <span class="hljs-comment"># specific test binary
cargo nextest run parsing <span class="hljs-comment"># filter by test nameThe output format is different from cargo test — nextest shows a live status indicator for each running test, then a summary of failures:
Compiling my_crate v0.1.0
Finished test profile [unoptimized + debuginfo] target(s) in 1.23s
Starting 42 tests across 3 binaries
PASS [ 0.002s] my_crate::tests::test_add
PASS [ 0.003s] my_crate::tests::test_subtract
FAIL [ 0.041s] my_crate::tests::test_database_connection
--- STDOUT: my_crate::tests::test_database_connection ---
thread 'main' panicked at 'connection refused'
---
Summary [ 0.045s] 42 tests run: 41 passed, 1 failedFailures are shown with full output at the end, not interleaved with other tests.
Configuration: .config/nextest.toml
nextest reads configuration from .config/nextest.toml at the workspace root:
[profile.default]
# How long before a test is considered hung
test-threads = "num-cpus" # default: number of logical CPUs
slow-timeout = { period = "60s", terminate-after = 3 }
fail-fast = false # keep running after first failure
[profile.default.junit]
path = "target/nextest/junit.xml"
[profile.ci]
# Stricter settings for CI
retries = 2
fail-fast = trueUse a profile with:
cargo nextest run --profile ciRetry Logic for Flaky Tests
Configure automatic retries for flaky tests:
[profile.ci]
retries = { backoff = "exponential", count = 3, delay = "1s", max-delay = "10s" }Or retry specific tests by name:
[[profile.ci.overrides]]
filter = 'test(network_)'
retries = 3Only tests matching the filter get retries. This lets you apply retry logic to known-flaky integration tests without masking real failures in unit tests.
Per-Test Timeouts
Set timeouts globally or per-test:
[profile.default]
test-threads = 8
[[profile.default.overrides]]
filter = 'test(slow_)'
slow-timeout = { period = "120s" } # warn after 2 minutes
[[profile.default.overrides]]
filter = 'test(unit_)'
slow-timeout = { period = "5s", terminate-after = 2 } # kill after 10sFiltering Tests
nextest has a rich filter expression language:
# Run tests in a specific package
cargo nextest run -p my_package
<span class="hljs-comment"># Run tests matching a name pattern
cargo nextest run <span class="hljs-built_in">test(parser)
<span class="hljs-comment"># Run tests NOT matching a pattern
cargo nextest run <span class="hljs-string">'not test(slow)'
<span class="hljs-comment"># Run tests in a specific file
cargo nextest run <span class="hljs-string">'test(/integration/)'
<span class="hljs-comment"># Combine filters
cargo nextest run <span class="hljs-string">'test(parse) and not test(slow)'JUnit Output for CI
Generate JUnit XML for test result dashboards:
[profile.ci.junit]
path = "target/nextest/junit.xml"cargo nextest run --profile ciUpload to GitHub Actions:
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: target/nextest/junit.xmlGitHub Actions Integration
A complete CI workflow with nextest:
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@nextest
# Build and test (nextest doesn't run doc tests)
- name: Run tests
run: cargo nextest run --profile ci --workspace
# Doc tests still need cargo test
- name: Run doc tests
run: cargo test --doc --workspaceLimitations
nextest doesn't support:
- Doc tests —
cargo test --docstill required for doc tests --nocapturewith live output — nextest captures stdout/stderr per-test by design;--no-capturedisables this but changes the output format
For doc tests in CI, run both:
cargo nextest run --workspace
cargo test --doc --workspaceArchive and Replay
nextest can archive test binaries and replay them on a different machine:
# Build and archive
cargo nextest archive --workspace --archive-file tests.tar.zst
<span class="hljs-comment"># Run the archive (no Rust toolchain needed on this machine)
cargo nextest run --archive-file tests.tar.zstThis is useful for separating build and test jobs in CI, or running tests on bare-metal machines without a full Rust install.
Comparing cargo test vs cargo nextest
| Feature | cargo test |
cargo nextest |
|---|---|---|
| Execution model | Threads in one process | Process per test |
| Output | Interleaved | Per-test, summary at end |
| Retries | None | Built-in |
| Per-test timeouts | None | Configurable |
| Doc tests | Yes | No (use cargo test --doc) |
| JUnit output | Needs third-party crate | Built-in |
| CI speedup | Baseline | 1.5-3× faster typical |
Migrating from cargo test
The migration is usually:
- Install nextest
- Replace
cargo testwithcargo nextest runin scripts and CI - Add
cargo test --docto cover doc tests - Add
.config/nextest.tomlwith retry and timeout configuration - Enable JUnit output if your CI dashboard supports it
Most test suites work without modification. If you have tests that rely on shared global state across tests (which is technically undefined behavior under cargo test too), they may fail under nextest's process isolation. Fix the isolation rather than work around it.
Monitoring Beyond Tests
nextest handles test execution in development and CI. For 24/7 production monitoring — ensuring your deployed service behaves correctly under real conditions — HelpMeTest runs your test scenarios continuously against live endpoints. No source code, no Rust toolchain needed.
Summary
- nextest is a drop-in replacement for
cargo testwith process-per-test isolation - Typical CI speedup: 1.5-3× on large test suites
- Built-in retry logic for flaky tests with backoff configuration
- Per-test timeouts prevent hung tests from blocking CI
- JUnit XML output for test result dashboards
- Doc tests still require
cargo test --doc - Archive/replay for split build+test CI pipelines