14.6Running tests

Last updated June 13, 2026

cargo test runs every test by default, which is what you want most of the time. But as a suite grows you'll want to run just one test, see the output of a test that's misbehaving, or skip the slow ones during quick iterations. This lesson covers the controls. They're small but they're the difference between a test suite you run constantly and one you avoid.

Filtering by name

Pass a string to cargo test and it runs only tests whose names contain that string:

cargo test adds
running 2 tests
test adds_positives ... ok
test adds_with_zero ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 2 filtered out; finished in 0.00s

Both tests containing "adds" ran; the rest were "filtered out" (the count appears in the summary). The match is a substring, not an exact name, so cargo test add runs everything with "add" anywhere in its path, and cargo test tests::adds_positives runs that one specific test. This is invaluable when you're working on one function and want to run just its tests in a tight loop, rather than waiting for the whole suite.

Seeing output from passing tests

By default, cargo test captures the output of passing tests: anything a passing test prints with println! is hidden, so the results stay clean. Only failing tests show their captured output (which is usually what you want, the noise from a failure). But when you're debugging and want to see prints from a passing test, pass --show-output:

cargo test -- --show-output

The -- is important: arguments before it go to Cargo, arguments after it go to the test runner. So cargo test adds -- --show-output means "run tests matching adds (to Cargo), and show their output (to the runner)." This is the test-running equivalent of the println!/dbg! debugging from lesson 3.4: sometimes the fastest way to understand why a test behaves oddly is to print from inside it and actually see the print.

Tip

Remember the -- divider: cargo test <filter> filters by name (Cargo's job), while flags for the test runner itself (--show-output, --test-threads, and others) go after --. Mixing them up is a common early stumble. cargo test --help shows Cargo's options; cargo test -- --help shows the runner's.

Ignoring slow tests

Some tests are slow (they hit the network, process a large file, run a long computation) and you don't want them in every quick run. Mark them #[ignore] and they're skipped by default:

#[test]
fn fast_check() {
    assert_eq!(2 + 2, 4);
}

#[test]
#[ignore]
fn slow_full_simulation() {
    // takes 30 seconds...
}

cargo test now runs fast_check and reports slow_full_simulation as ignored. When you do want the slow ones, cargo test -- --ignored runs only the ignored tests, and cargo test -- --include-ignored runs everything. This lets you keep slow tests in the suite (so they're not forgotten) while keeping the default run fast enough to use constantly.

Tests run in parallel

By default, cargo test runs your tests in parallel, on multiple threads, for speed. This is usually invisible and beneficial, but it has one consequence worth knowing: tests must be independent, exactly the quality lesson 14.1 called for. If two tests both write to the same file, or depend on a shared global, running them at the same time can make them interfere and fail unpredictably. The fix is almost always to make each test self-contained (use separate files, fresh data per test), not to disable parallelism.

When tests genuinely can't run in parallel and you can't easily fix that, you can force them to run one at a time:

cargo test -- --test-threads=1

--test-threads=1 runs tests sequentially. Reach for it as a diagnostic (if a test passes alone but fails in the full run, parallelism and shared state are the likely culprit) rather than a permanent crutch. Independent tests that can run in any order, in parallel, are the goal.

Best practice

Write tests independent enough to run in parallel, and lean on name filtering to run just what you're working on. Keep slow tests in the suite behind #[ignore] rather than deleting them, so they still exist for the occasional full run (and CI). The aim is a default cargo test fast and clean enough that running it is a reflex, not a decision.

Quiz time

Question #1

What does cargo test add do, and is the match exact or a substring?

Show solution

It runs only the tests whose names contain "add" (a substring match), and reports the rest as "filtered out." So it would run adds_positives, add_with_zero, and any other test with "add" in its path. To run one specific test, give more of its name, e.g. cargo test tests::adds_positives.

Question #2

By default, can you see println! output from a passing test? How do you change that, and what's the role of the -- in the command?

Show solution

No: output from passing tests is captured (hidden) by default; only failing tests show their output. Use cargo test -- --show-output to see prints from passing tests. The -- separates Cargo's arguments (before it) from the test runner's arguments (after it), so --show-output reaches the runner.

Question #3

A test passes when run alone (cargo test that_test) but fails when you run the whole suite. What's a likely cause, and what command helps confirm it?

Show solution

Likely a shared-state dependency between tests combined with parallel execution: another test running at the same time interferes (a shared file, global, or resource). Confirm with cargo test -- --test-threads=1, which runs tests sequentially; if it then passes, parallelism plus shared state is the cause. The real fix is to make the tests independent (lesson 14.1), not to leave threads at 1.

That completes the testing toolkit: writing tests, organizing them, integration and doc tests, and running them well. The summary and quiz are next, and then chapter 15 returns to the type system with generics, the feature that lets one function or type work for many types at once, and the direct setup for traits in chapter 16.