24.2 Writing Basic Tests

In Rust, tests are functions marked with the #[test] attribute. The test runner executes these functions. A test passes if its function completes execution without panicking; it fails if the function panics.

24.2.1 The #[test] Attribute

fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[test]
fn test_addition_success() {
    let result = add(2, 2);
    assert_eq!(result, 4); // Passes if 2 + 2 == 4
}

#[test]
fn test_addition_failure() {
    let result = add(2, 2);
    // This assertion fails because 4 != 5, causing the function to panic.
    assert_eq!(result, 5);
}
  • The #[test] attribute identifies test_addition_success and test_addition_failure as test functions.
  • Test functions typically take no arguments and return () (the unit type), although returning Result is also possible (see Section 24.5.2).

24.2.2 Assertion Macros

Rust’s standard library provides macros for asserting conditions within tests:

  • assert!(expression): Panics if expression evaluates to false. Suitable for simple boolean conditions.
  • assert_eq!(left, right): Panics if left != right. This is the most frequently used assertion. Requires that the types implement the PartialEq and Debug traits (the latter for printing values upon failure).
  • assert_ne!(left, right): Panics if left == right. Also requires PartialEq and Debug.

These macros can accept optional arguments (after the mandatory ones) for a custom failure message, formatted using the same syntax as println!:

#[test]
fn test_custom_message() {
    let width = 15;
    assert!(width >= 0 && width <= 10, "Width ({}) is out of range [0, 10]", width);
}

24.2.3 Running Tests with cargo test

The command cargo test compiles the project in a test configuration (which includes code marked with #[cfg(test)]) and runs all discovered tests (unit, integration, and documentation tests).

$ cargo test
   Compiling my_crate v0.1.0 (...)
    Finished test [unoptimized + debuginfo] target(s) in ...s
     Running unittests src/lib.rs (...)

running 2 tests
test tests::test_addition_success ... ok
test tests::test_addition_failure ... FAILED

failures:

---- tests::test_addition_failure stdout ----
thread 'tests::test_addition_failure' panicked at src/lib.rs:16:5:
assertion failed: `(left == right)`
  left: `4`,
 right: `5`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::test_addition_failure

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in ...s

error: test failed, to rerun pass '--lib'

The output clearly shows test progress, failures with assertion details (values, file, line number), and a final summary.