Unit Testing

Test Output and Diagnostics

By default, Rust's test harness captures standard output and standard error produced by println!, eprintln!, and dbg! during each test:

  • If a test passes, its captured output is silently discarded so the terminal displays only a clean test name ... ok line.
  • If a test fails, the harness prints the entire captured stdout/stderr buffer for that test above the panic message to aid debugging.
Test Execution under `cargo test`:
  println!("step 1") ---> [ Thread-Local Capture Buffer ]
                                    |
         +--------------------------+--------------------------+
         |                                                     |
   Test PASSES                                           Test FAILS
   Buffer discarded;                                     Buffer dumped to terminal
   prints only: `test foo ... ok`                        under `---- foo stdout ----`

Controlling Output Capture via Test Harness Flags

To pass flags to the compiled test binary itself (rather than to cargo), place them after -- in the cargo test command:

Command Behavior
cargo test Captures stdout/stderr; prints output only for failing tests.
cargo test -- --show-output Captures output during execution (preventing interleaved lines across threads), then prints captured output for both passing and failing tests at the end.
cargo test -- --nocapture Disables output capturing completely; println! writes directly to the terminal in real time (output from parallel tests may interleave).
pub fn compute_checksum(bytes: &[u8]) -> u32 {
    let mut sum = 0u32;
    for (i, &b) in bytes.iter().enumerate() {
        sum = sum.wrapping_add((b as u32) * (i as u32 + 1));
    }
    sum
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn checksum_logs_intermediate_trace() {
        let input = b"rust";
        println!("Computing checksum for {:?}", std::str::from_utf8(input));
        let result = compute_checksum(input);
        println!("Computed checksum = {result}");
        assert_eq!(result, 114 * 1 + 117 * 2 + 115 * 3 + 116 * 4);
    }
}

fn main() {
    println!("Checksum of 'rust': {}", compute_checksum(b"rust"));
}
Checksum of 'rust': 1157

Running cargo test -- --show-output displays the ---- stdout ---- section even though the test passed:

running 1 test
test tests::checksum_logs_intermediate_trace ... ok

successes:

---- tests::checksum_logs_intermediate_trace stdout ----
Computing checksum for Ok("rust")
Computed checksum = 1157


successes:
    tests::checksum_logs_intermediate_trace

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

Enabling Stack Backtraces on Test Failure (RUST_BACKTRACE)

When a test panics deep inside a call stack, set the RUST_BACKTRACE environment variable to see the exact sequence of stack frames leading to the failure:

  • RUST_BACKTRACE=1 cargo test: Prints a concise stack trace filtering out Rust runtime setup frames.
  • RUST_BACKTRACE=full cargo test: Prints every frame including memory addresses and standard library internals.

Why Background Threads May Bypass Capture

The test harness captures println! and eprintln! by installing a thread-local output buffer on the test thread. If your test spawns raw OS threads (std::thread::spawn) or child processes (std::process::Command with Stdio::inherit()), output written directly to file descriptors 1 (stdout) or 2 (stderr) may bypass the capture buffer unless --nocapture is used.