24.6 Test Organization
24.6.1 Unit Tests
Unit tests are usually placed in the same file or module as the code under test:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_xyz() {
// ...
}
}
Benefits:
- Test Private Functions: You can access private items in the same module.
- Convenience: Code and tests live side by side.
24.6.2 Integration Tests
Integration tests live in a top-level tests/
directory. Each .rs
file there is compiled as a separate crate that imports your library:
my_project/
├── src/
│ └── lib.rs
└── tests/
├── test_basic.rs
└── test_advanced.rs
Inside test_basic.rs
:
use my_project; // The name of your crate
#[test]
fn test_something() {
let result = my_project::some_public_function();
assert_eq!(result, 42);
}
Integration tests validate public APIs. You can split them across multiple files for clarity.
Common Functionality for Integration Tests
If your integration tests share functionality, you might place common helpers in a file named tests/common/mod.rs
, and import them in your test files. Because mod.rs
follows a special naming convention, it won’t be treated as a standalone test file.
Running a Single Integration Test File
cargo test --test test_basic
This command runs only the tests in test_basic.rs
.
Integration Tests for Binary Crates
If you have only a binary crate (e.g., src/main.rs
without src/lib.rs
), you cannot directly import functions from main.rs
into an integration test. Binary crates produce executables but do not expose APIs to other crates.
A common solution is to move your core functionality into a library (src/lib.rs
), letting main.rs
handle only top-level execution. This allows you to write standard integration tests against the library crate.