Testing & Documentation
Testing and documentation are part of the language and toolchain, not add-ons.
#[test] unit tests, tests/ integration tests, and /// doc-tests all run with one command: cargo test.
Unit Tests
Unit tests live in a #[cfg(test)] module inside the same file as the code they test. Annotate each test with #[test]; cargo runs them all with one command:
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_adds() {
assert_eq!(add(2, 2), 4);
}
#[test]
fn it_adds_zero() {
assert_eq!(add(0, 7), 7);
}
}
cargo test # compiles and runs every #[test]
Assertions
Rust ships a family of assertion macros. assert! checks a condition, assert_eq!/assert_ne! compare two values with debug formatting, and #[should_panic] documents expected failures:
#[cfg(test)]
mod tests {
#[test]
fn comparisons() {
assert_eq!(4, 4); // equal
assert_ne!(4, 5); // not equal
assert!(2 + 2 == 4); // boolean condition
}
#[test]
#[should_panic(expected = "attempt to divide by zero")]
fn divides_by_zero() {
let _n = 1i32 / 0; // panics as expected
}
}
Integration Tests
Integration tests exercise your crate as an external consumer would, from files under tests/. They import your library crate by name:
hello/
├── src/lib.rs
└── tests/
└── integration.rs
// tests/integration.rs — imports the library crate `hello`
use hello::add;
#[test]
fn test_through_public_api() {
assert_eq!(add(10, 32), 42);
}
Doc-tests
Code blocks inside /// documentation comments are compiled and run by cargo test too. This keeps docs and code from drifting apart:
/// Adds two unsigned integers.
///
/// # Examples
///
/// ```
/// let result = my_crate::add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(left: usize, right: usize) -> usize {
left + right
}
cargo test --doc # run only the doc-tests
cargo doc --open # build the rendered docs
Practice move: add a deliberately wrong
assert_eq! to the lab examples, run cargo test, and read the diff the assertion prints.