Test Organization
Rust categorizes tests as unit tests (small, focused, test one module in isolation, can test private interfaces) and integration tests (external to your library, use public API only, test multiple modules together).
Unit Tests
Place unit tests in each file with the code they test, in a tests
module annotated with #[cfg(test)]
.
The Tests Module and #[cfg(test)]
The #[cfg(test)]
annotation compiles and runs test code only with cargo test
, not cargo build
:
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
Testing Private Functions
Rust allows testing private functions since tests are just Rust code in the same crate:
pub fn add_two(a: u64) -> u64 {
internal_adder(a, 2)
}
fn internal_adder(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn internal() {
let result = internal_adder(2, 2);
assert_eq!(result, 4);
}
}
The use super::*;
brings parent module items into scope.
Integration Tests
Create integration tests in a tests
directory at the project root (alongside src
). Each file in tests
is compiled as a separate crate.
The tests Directory
Project structure:
adder
├── Cargo.lock
├── Cargo.toml
├── src
│ └── lib.rs
└── tests
└── integration_test.rs
use adder::add_two;
#[test]
fn it_adds_two() {
let result = add_two(2);
assert_eq!(result, 4);
}
Each test file gets its own section in test output. Run specific integration test file:
$ cargo test --test integration_test
Submodules in Integration Tests
Files in tests
directory are separate crates and don’t share the same behavior as src
files.
For shared helper functions, use subdirectories instead of top-level files:
├── tests
├── common
│ └── mod.rs // Shared code
└── integration_test.rs
Use from integration tests:
use adder::add_two;
mod common;
#[test]
fn it_adds_two() {
common::setup();
let result = add_two(2);
assert_eq!(result, 4);
}
Integration Tests for Binary Crates
Binary crates with only src/main.rs
cannot have integration tests that import functions with use
statements. Structure binary projects with logic in src/lib.rs
and a thin src/main.rs
that calls the library functions.
Summary
Rust’s testing features support both detailed unit testing and broader integration testing. Unit tests verify individual modules (including private functions), while integration tests validate public API behavior. This testing strategy helps ensure code correctness as you refactor and extend functionality.