| name | rust-testing |
| description | | Use when this capability is needed. |
Rust Testing Core Knowledge
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: rust for comprehensive documentation.
Full Reference: See advanced.md for HTTP Testing with wiremock, Property-Based Testing with proptest, Benchmarks with criterion, and Test Coverage with cargo-tarpaulin.
When NOT to Use This Skill
- JavaScript/TypeScript Projects - Use
vitest or jest
- Java Projects - Use
junit for Java testing
- Python Projects - Use
pytest for Python
- Go Projects - Use
go-testing skill
- E2E Browser Testing - Use Playwright or Selenium
Basic Testing
Unit Tests
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
pub fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
None
} else {
Some(a / b)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn test_divide() {
assert_eq!(divide(10.0, 2.0), Some(5.0));
}
#[test]
fn test_divide_by_zero() {
assert_eq!(divide(10.0, 0.0), None);
}
}
Running Tests
cargo test
cargo test test_add
cargo test tests::
cargo test -- --nocapture
cargo test -- --test-threads=1
cargo test -- --ignored
Assertions
#[cfg(test)]
mod tests {
#[test]
fn test_assertions() {
assert_eq!(2 + 2, 4);
assert_ne!(2 + 2, 5);
assert!(true);
assert!(!false);
assert!(1 + 1 == 2, "Math is broken!");
assert_eq!(2 + 2, 4, "Expected {} but got {}", 4, 2 + 2);
}
#[test]
fn test_floating_point() {
let result = 0.1 + 0.2;
let expected = 0.3;
assert!((result - expected).abs() < 1e-10);
}
}
Expected Panics
pub fn divide_or_panic(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("Cannot divide by zero!");
}
a / b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn test_panic() {
divide_or_panic(10, 0);
}
#[test]
#[should_panic(expected = "Cannot divide by zero")]
fn test_panic_message() {
divide_or_panic(10, 0);
}
}
Result-Based Tests
#[cfg(test)]
mod tests {
#[test]
fn test_with_result() -> Result<(), String> {
if 2 + 2 == 4 {
Ok(())
} else {
Err("Math failed".to_string())
}
}
}
Ignored Tests
#[test]
#[ignore]
fn expensive_test() {
std::thread::sleep(std::time::Duration::from_secs(60));
}
#[test]
#[ignore = "requires database connection"]
fn test_database() {
}
Integration Tests
use my_crate::{add, divide};
#[test]
fn test_add_integration() {
assert_eq!(add(100, 200), 300);
}
pub fn setup() {
}
mod common;
#[test]
fn test_with_setup() {
common::setup();
}
Async Testing
Tokio Test
[dev-dependencies]
tokio = { version = "1", features = ["full", "test-util"] }
use tokio::time::{sleep, Duration};
async fn async_add(a: i32, b: i32) -> i32 {
sleep(Duration::from_millis(10)).await;
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_async_add() {
let result = async_add(2, 3).await;
assert_eq!(result, 5);
}
#[tokio::test]
async fn test_multiple_async() {
let (a, b) = tokio::join!(
async_add(1, 2),
async_add(3, 4)
);
assert_eq!(a, 3);
assert_eq!(b, 7);
}
}
Testing with Time
use tokio::time::{self, Duration, Instant};
async fn delayed_operation() -> &'static str {
time::sleep(Duration::from_secs(10)).await;
"done"
}
#[cfg(test)]
mod tests {
use super::*;
use tokio::time::{pause, advance};
#[tokio::test]
async fn test_with_time_control() {
pause();
let start = Instant::now();
let future = delayed_operation();
advance(Duration::from_secs(10)).await;
let result = future.await;
assert_eq!(result, "done");
assert!(start.elapsed() < Duration::from_secs(1));
}
}
Mocking with mockall
[dev-dependencies]
mockall = "0.12"
Basic Mocking
use mockall::{automock, predicate::*};
#[automock]
trait Database {
fn get(&self, key: &str) -> Option<String>;
fn set(&mut self, key: &str, value: &str) -> bool;
}
struct Service<D: Database> {
db: D,
}
impl<D: Database> Service<D> {
fn new(db: D) -> Self {
Self { db }
}
fn get_value(&self, key: &str) -> String {
self.db.get(key).unwrap_or_else(|| "default".to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_value_exists() {
let mut mock = MockDatabase::new();
mock.expect_get()
.with(eq())
.()
.(|_| (.()));
= Service::(mock);
(service.(), );
}
() {
= MockDatabase::();
mock.()
.(())
.()
.(|_| );
= Service::(mock);
(service.(), );
}
}
Mock Expectations
#[cfg(test)]
mod tests {
use super::*;
use mockall::Sequence;
#[test]
fn test_call_count() {
let mut mock = MockDatabase::new();
mock.expect_get()
.times(3)
.returning(|_| Some("value".to_string()));
let service = Service::new(mock);
service.get_value("a");
service.get_value("b");
service.get_value("c");
}
#[test]
fn test_call_sequence() {
let mut seq = Sequence::new();
let mut mock = MockDatabase::new();
mock.expect_get()
.with(eq("first"))
.times(1)
.in_sequence(& seq)
.(|_| (.()));
mock.()
.(())
.()
.(& seq)
.(|_| (.()));
= Service::(mock);
(service.(), );
(service.(), );
}
}
Async Mock
use mockall::{automock, predicate::*};
use async_trait::async_trait;
#[async_trait]
#[automock]
trait AsyncDatabase {
async fn get(&self, key: &str) -> Option<String>;
async fn set(&self, key: &str, value: &str) -> bool;
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_async_mock() {
let mut mock = MockAsyncDatabase::new();
mock.expect_get()
.with(eq("key"))
.times(1)
.returning(|_| Some("value".to_string()));
let result = mock.get("key").await;
assert_eq!(result, Some(.()));
}
}
Checklist
Anti-Patterns
| Anti-Pattern | Why It's Bad | Solution |
|---|
| Testing private functions | Coupled to implementation | Test through public API |
| Not using #[should_panic] | Missing error validation | Test expected panics explicitly |
| Shared mutable state in tests | Flaky tests | Use test isolation |
| Not using Result<()> in tests | Can't use ? operator | Return Result<(), Error> |
| Ignoring async tests | Wrong runtime behavior | Use #[tokio::test] for async |
| Not benchmarking | Performance regressions | Use Criterion for benchmarks |
| Missing property tests | Edge cases missed | Use proptest for algorithms |
Quick Troubleshooting
| Problem | Likely Cause | Solution |
|---|
| "test result: FAILED. 0 passed" | Panic in test | Check panic message, add proper assertions |
| Async test timeout | Missing await or infinite loop | Ensure all futures are awaited |
| Mock expectation failed | Wrong method calls | Verify mockall expectations |
| "cannot find derive macro" | Missing dev-dependency | Add mockall to [dev-dependencies] |
| Benchmark not running | Wrong harness setting | Set harness = false in [[bench]] |
| Coverage tool crashes | Incompatible version | Use cargo-tarpaulin or llvm-cov |
Reference Documentation
Source: claude-dev-suite/claude-dev-suite — distributed by TomeVault.