Write unit tests by placing #[test] annotated functions inside a tests module within your source file, then run them with cargo test. Rust automatically compiles and executes these tests, reporting failures with clear error messages and stack traces.
For simple logic, place the test module directly in the file being tested, typically at the bottom or in a separate mod tests block. This keeps tests close to the code they verify.
// src/lib.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_positive() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn test_add_negative() {
assert_eq!(add(-1, -1), -2);
}
}
Run the tests using the Cargo command line tool. Cargo handles compilation, execution, and output formatting automatically.
cargo test
If you need to test private functions or structs, ensure the tests module is in the same file as the code under test so it has access to private items. For larger projects, you might move tests to integration tests in a tests/ directory at the crate root, but unit tests remain the standard for verifying internal logic.
Use assert! for boolean conditions, assert_eq! for equality checks, and assert_ne! for inequality. For expected panics, wrap the call in std::panic::catch_unwind or use the #[should_panic] attribute on the test function.
#[test]
#[should_panic(expected = "index out of bounds")]
fn test_panic_on_invalid_index() {
let v = vec![1, 2, 3];
let _ = v[10];
}
If a test fails, Cargo outputs the specific assertion that failed, the expected value, and the actual value, making debugging straightforward. You can also run specific tests by name using cargo test test_name to speed up development cycles.