Rust testing
Use when writing unit/integration tests, setting up test infrastructure, mocking with wiremock/mockall, or designing test structure in Rust.From its SKILL.md
npx -y skills add longzhi/rust-coding-standards --skill rust-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.1 KB, as published. Nobody here has run it
Rust Testing Standards
Test Categories
| Type | Location | Dependencies |
|---|---|---|
| Unit tests | #[cfg(test)] module in source file | None (no external deps) |
| Integration tests | tests/ directory | May need database/services |
Commands
cargo test # all tests
cargo test --lib # unit tests only
cargo test --test '*' # integration tests only
cargo test -- --nocapture # show stdout
Unit Tests
Place in a #[cfg(test)] module at the bottom of the source file:
impl Order {
pub fn can_cancel(&self) -> bool {
matches!(self.status, OrderStatus::Pending | OrderStatus::Processing)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_can_cancel_pending_order() {
let order = Order { status: OrderStatus::Pending, ..Default::default() };
assert!(order.can_cancel());
}
}
Naming Convention
// Format: test_{feature}_{scenario}_{expected}
fn test_place_order_with_insufficient_balance_returns_error() { }
// Or "should" style
fn should_return_error_when_balance_insufficient() { }
Integration Tests
tests/
├── common/
│ └── mod.rs # shared helpers (setup_test_db, etc.)
├── api/
│ └── orders_test.rs
└── integration/
└── order_flow_test.rs
HTTP Test Example
#[tokio::test]
async fn test_create_order_success() {
let pool = setup_test_db().await;
let server = TestServer::new(create_test_app(pool.clone()).await).unwrap();
let response = server
.post("/orders")
.add_header("Authorization", format!("Bearer {}", api_key))
.json(&json!({ "product_id": "prod-001", "quantity": 2 }))
.await;
response.assert_status(StatusCode::CREATED);
}
Mocking
Trait Abstraction (hand-written mock)
#[async_trait]
pub trait ExternalApiClient: Send + Sync {
async fn submit(&self, req: &SubmitRequest) -> Result<SubmitResponse, Error>;
}
// Test implementation
pub struct MockExternalApiClient {
pub responses: Arc<Mutex<Vec<Result<SubmitResponse, Error>>>>,
}
mockall Crate (recommended alternative)
For most cases, prefer mockall over hand-written mocks — it auto-generates mock structs from traits:
use mockall::automock;
#[automock]
#[async_trait]
pub trait ExternalApiClient: Send + Sync {
async fn submit(&self, req: &SubmitRequest) -> Result<SubmitResponse, Error>;
}
#[tokio::test]
async fn test_submit_delegates_to_client() {
let mut mock = MockExternalApiClient::new();
mock.expect_submit()
.returning(|_| Ok(SubmitResponse { id: "abc-123".into() }));
let service = OrderService::new(Arc::new(mock));
let result = service.process(&request).await.unwrap();
assert_eq!(result.id, "abc-123");
}
wiremock (HTTP-level mocking)
#[tokio::test]
async fn test_external_api_client() {
let mock_server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/api/process"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "abc-123"
})))
.mount(&mock_server)
.await;
let client = ExternalApiClient::new(&mock_server.uri());
let result = client.submit(&request).await.unwrap();
assert_eq!(result.id, "abc-123");
}
Key Coverage Targets
- State machine transitions: e.g., Order status flow (Pending -> Confirmed -> Shipped -> Delivered)
- Billing logic: balance deduction, refunds, proration
- Authorization checks: API key validation, role-based access
- Boundary conditions: null/empty values, limits exceeded, concurrency
Property-Based Testing (advanced)
For logic with wide input ranges, consider proptest to auto-generate test cases:
use proptest::prelude::*;
proptest! {
#[test]
fn test_discount_never_exceeds_total(
price in 1u64..10_000,
discount_pct in 0u8..=100,
) {
let discounted = apply_discount(price, discount_pct);
prop_assert!(discounted <= price);
}
}
proptest is especially useful for parsers, serialization round-trips, and numeric invariants.
Anti-Patterns
// WRONG: unwrap without context
let result = operation().unwrap();
// RIGHT: use expect
let result = operation().expect("operation should succeed with valid input");
// WRONG: hard-coded sleep
std::thread::sleep(Duration::from_secs(1));
// RIGHT: conditional wait with timeout
tokio::time::timeout(Duration::from_secs(5), wait_for_condition()).await;
// WRONG: tests depend on execution order
// RIGHT: each test is fully independent
Checklist
- Unit tests live in
#[cfg(test)]modules - Integration tests live in
tests/directory - External dependencies are mocked (mockall or hand-written traits)
- Test database is cleaned between runs
- Async tests use
#[tokio::test] - Tests are independent of each other