24.3 Creating and Executing Tests

Unit tests can reside within your application code, while integration tests typically assume a library-like structure. Rust compiles tests under the test profile, which instructs Cargo to compile test modules and any test binaries.

24.3.1 Structure of a Test Function

Any ordinary, parameterless function can become a test by adding the #[test] attribute:

#[test]
fn test_something() {
    // Arrange: set up data
    // Act: call the function under test
    // Assert: verify the result
}
  • #[test] tells the compiler and test harness to run this function when you execute cargo test.
  • A test fails if it panics (e.g., via assert! or panic!), and passes otherwise.

24.3.2 Default Test Templates

When you create a new library with:

cargo new adder --lib

Cargo includes a sample test in src/lib.rs:

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

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

The #[cfg(test)] attribute ensures the tests module is compiled only during testing (and not in normal builds). Keeping all unit tests in a dedicated test module separates testing functionality from main code. You can also add test-specific helper functions here without triggering warnings about unused functions in production code.

24.3.3 Using assert!, assert_eq!, and assert_ne!

Rust provides several macros to verify behavior:

  • assert!(condition): Fails if condition is false.
  • assert_eq!(left, right): Fails if left != right. Requires PartialEq and Debug.
  • assert_ne!(left, right): Fails if left == right.

You can also provide custom messages:

#[test]
fn test_assert_macros() {
    let x = 3;
    let y = 4;
    assert!(x + y == 7, "x + y should be 7, but got {}", x + y);
    assert_eq!(x * y, 12);
    assert_ne!(x, y);
}

24.3.4 Example: Passing and Failing Tests

fn multiply(a: i32, b: i32) -> i32 {
    a * b
}

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

    #[test]
    fn test_multiply_passes() {
        assert_eq!(multiply(3, 4), 12);
    }

    #[test]
    fn test_multiply_fails() {
        // This will fail:
        assert_eq!(multiply(3, 4), 15);
    }
}

When you run cargo test, you’ll see one passing test and one failing test.