| name | rust-testing |
| description | Guide for writing effective Rust tests with cargo test, unit tests, integration tests, and test organization |
| triggers | ["rust test","cargo test","unit test","integration test","test coverage","#[test]"] |
Rust Testing
Comprehensive guide for testing Rust code effectively.
Quick Start
Write unit tests inline with your code:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
#[test]
#[should_panic(expected = "divide by zero")]
fn test_panic() {
divide(10, 0);
}
}
Run tests with:
cargo test
cargo test test_name
cargo test -- --nocapture
Test Organization
Unit tests - In the same file as the code:
- Use
#[cfg(test)] module
- Test private functions
- Fast, isolated
Integration tests - In tests/ directory:
- Test public API only
- Each file is a separate crate
- Example:
tests/integration_test.rs
Doc tests - In documentation comments:
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
Common Patterns
Testing Results:
#[test]
fn test_result() -> Result<(), String> {
let value = parse_config("config.toml")?;
assert_eq!(value.port, 8080);
Ok(())
}
Testing with setup/teardown:
use tempfile::TempDir;
#[test]
fn test_with_cleanup() {
let dir = TempDir::new().unwrap();
}
Async tests (with tokio):
#[tokio::test]
async fn test_async() {
let result = fetch_data().await;
assert!(result.is_ok());
}
Best Practices
- One assertion per test - Makes failures clear
- Test edge cases - Empty inputs, boundaries, errors
- Use descriptive names -
test_parse_empty_string_returns_error
- Avoid test interdependence - Each test should be isolated
- Mock external dependencies - Use traits for dependency injection
Useful Attributes
#[test] - Mark as test function
#[should_panic] - Expect panic
#[ignore] - Skip by default (run with cargo test -- --ignored)
#[cfg(test)] - Compile only in test mode
Coverage
Generate coverage with cargo-tarpaulin:
cargo install cargo-tarpaulin
cargo tarpaulin --out Html
Aim for >80% coverage on critical paths.