How to write unit tests

Write unit tests by defining a `tests` module within your source file (or in a separate `tests/` directory for integration tests) and using the `#[test]` attribute on functions that assert expected behavior.

Write unit tests by defining a tests module within your source file (or in a separate tests/ directory for integration tests) and using the #[test] attribute on functions that assert expected behavior. Run them with cargo test, which automatically compiles and executes all test functions, reporting failures with clear diffs.

For simple unit tests, place the tests module at the bottom of your source file. This keeps tests close to the code they verify and allows them to access private items. Use the assert! macro for boolean checks, assert_eq! for value comparisons, and assert_ne! to ensure inequality.

// src/lib.rs

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

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

    #[test]
    fn test_add_positive_numbers() {
        assert_eq!(add(2, 2), 4);
    }

    #[test]
    fn test_add_negative_numbers() {
        assert_eq!(add(-2, -2), -4);
    }

    #[test]
    #[should_panic]
    fn test_divide_by_zero() {
        // This test expects the code to panic
        let _ = 1 / 0;
    }
}

Run the tests with cargo test. To run a specific test, use cargo test test_add_positive_numbers. For more complex scenarios where you need to test private functions or mock dependencies, the #[cfg(test)] module is essential because it only compiles when running tests, keeping your production binary lean.

If your project grows, move tests to a separate tests/ directory for integration tests. These files are compiled as separate crates, meaning they can only access public APIs, simulating how external users interact with your library.

// tests/integration_test.rs

use my_crate::add;

#[test]
fn test_add_from_public_api() {
    assert_eq!(add(10, 20), 30);
}

To run only integration tests, use cargo test --test integration_test. Remember to handle errors gracefully in tests; if a function returns a Result, unwrap it only when you expect success, or use assert!(result.is_ok()) to check the type. For testing panics, the #[should_panic] attribute is useful, but prefer testing the Result return value when possible to get more specific failure messages.