| name | rust-testing |
| description | Rust testing patterns including unit tests, integration tests, async testing, property-based testing, mocking, and coverage. Follows TDD methodology. Use when this capability is needed. |
| metadata | {"author":"bl1nk-bot"} |
Rust Testing Patterns
Comprehensive Rust testing patterns for writing reliable, maintainable tests following TDD methodology.
When to Use
- Writing new Rust functions, methods, or traits
- Adding test coverage to existing code
- Creating benchmarks for performance-critical code
- Implementing property-based tests for input validation
- Following TDD workflow in Rust projects
How It Works
- Identify target code — Find the function, trait, or module to test
- Write a test — Use
#[test] in a #[cfg(test)] module, rstest for parameterized tests, or proptest for property-based tests
- Mock dependencies — Use mockall to isolate the unit under test
- Run tests (RED) — Verify the test fails with the expected error
- Implement (GREEN) — Write minimal code to pass
- Refactor — Improve while keeping tests green
- Check coverage — Use cargo-llvm-cov, target 80%+
TDD Workflow for Rust
The RED-GREEN-REFACTOR Cycle
RED → Write a failing test first
GREEN → Write minimal code to pass the test
REFACTOR → Improve code while keeping tests green
REPEAT → Continue with next requirement
Step-by-Step TDD in Rust
pub fn add(a: i32, b: i32) -> i32 { todo!() }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() { assert_eq!(add(2, 3), 5); }
}
pub fn add(a: i32, b: i32) -> i32 { a + b }
```text
## Unit Tests
### Module-Level Test Organization
```rust
pub struct User {
pub name: String,
pub email: String,
}
impl User {
pub fn new(name: impl Into<String>, email: impl Into<String>) -> Result<Self, String> {
let email = email.into();
if !email.contains('@') {
return Err(format!("invalid email: {email}"));
}
Ok(Self { name: name.into(), email })
}
pub fn display_name(&self) -> & {
&.name
}
}
tests {
super::*;
() {
= User::(, ).();
(user.(), );
(user.email, );
}
() {
= User::(, );
(result.());
(result.().());
}
}
```text
### Assertion Macros
```rust
( + , );
( + , );
([, , ].(&));
(value, , );
(( + - ).() < ::EPSILON);
```text
## Error and Panic Testing
### Testing `` Returns
```rust
() {
= ();
(result.());
= result.();
(matches!(err, ConfigError::(_)));
}
() <(), < std::error::Error>> {
= ()?;
(config.port, );
(())
}
```text
### Testing Panics
```rust
() {
(&[]);
}
() {
: <> = [];
= v[];
}
```text
## Integration Tests
### File Structure
```text
my_crate/
├── src/
│ └── lib.rs
├── tests/ # Integration tests
│ ├── api_test.rs # Each file is a separate test binary
│ ├── db_test.rs
│ └── common/ # Shared test utilities
│ └── .rs
```text
### Writing Integration Tests
```rust
my_crate::{App, Config};
() {
= Config::();
= App::(config);
= app.();
(response.status, );
(response.body, );
}
```text
## Async Tests
### With Tokio
```rust
() {
= TestClient::().;
= client.().;
(result.());
(result.().items.(), );
}
() {
std::time::Duration;
= tokio::time::(
Duration::(),
(),
).;
(result.(), );
}
```text
## Test Organization Patterns
### Parameterized Tests with `rstest`
```rust
rstest::{rstest, fixture};
( input: &, expected: ) {
(input.(), expected);
}
() TestDb {
TestDb::()
}
(test_db: TestDb) {
test_db.(, );
(test_db.(), (.()));
}
```text
### Test Helpers
```rust
tests {
super::*;
(name: &) User {
User::(name, &()).()
}
() {
= ();
(user.(), );
}
}
```text
## Property-Based Testing with `proptest`
### Basic Property Tests
```rust
proptest::prelude::*;
proptest! {
(input ) {
= (&input);
= (&encoded).();
(input, decoded);
}
( vec prop::collection::(any::<>(), ..)) {
= vec.();
vec.();
(vec.(), original_len);
}
( vec prop::collection::(any::<>(), ..)) {
vec.();
vec.() {
(window[] <= window[]);
}
}
}
```text
### Custom Strategies
```rust
proptest::prelude::*;
() <Value = > {
(, )
.(|(user, domain)| ())
}
proptest! {
(email ()) {
(User::(, &email).());
}
}
```text
## Mocking with `mockall`
### Trait-Based Mocking
```rust
mockall::{automock, predicate::eq};
{
(&, id: ) <User>;
(&, user: &User) <(), StorageError>;
}
() {
= MockUserRepository::();
mock.()
.(())
.()
.(|_| (User { id: , name: .() }));
= UserService::(::(mock));
= service.().();
(user.name, );
}
() {
= MockUserRepository::();
mock.()
.(|_| );
= UserService::(::(mock));
(service.().());
}
```text
## Doc Tests
### Executable Documentation
```rust
(a: , b: ) {
a + b
}
(input: &) <Config, ParseError> {
todo!()
}
Benchmarking with Criterion
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]
name = "benchmark"
harness = false
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn fibonacci(n: u64) -> u64 {
match n {
0 | 1 => n,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
fn bench_fibonacci(c: &mut Criterion) {
c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
}
criterion_group!(benches, bench_fibonacci);
criterion_main!(benches);
```text
## Test Coverage
### Running Coverage
```bash
# Install: cargo install cargo-llvm-cov (or use taiki-e/install-action in CI)
cargo llvm-cov # Summary
cargo llvm-cov --html # HTML report
cargo llvm-cov --lcov > lcov.info # LCOV format for CI
cargo llvm-cov --fail-under-lines 80 # Fail if below threshold
```text
### Coverage Targets
| Code Type | Target |
|-----------|--------|
| Critical business logic | 100% |
| Public API | 90%+ |
| General code | 80%+ |
| Generated / FFI bindings | Exclude |
## Testing Commands
```bash
cargo test # Run all tests
cargo test -- --nocapture # Show println output
cargo test test_name # Run tests matching pattern
cargo test --lib # Unit tests only
cargo test --test api_test # Integration tests only
cargo test --doc # Doc tests only
cargo test --no-fail-fast # Don't stop on first failure
cargo test -- --ignored # Run ignored tests
```text
## Best Practices
**DO:**
- Write tests (TDD)
- Use `` modules tests
- Test behavior, not implementation
- Use descriptive test names that explain the scenario
- Prefer `` over `` error messages
- Use `?` tests that `` error output
- Keep tests independent — no shared mutable state
**DON:**
- Use `` when you can test `::()` instead
- Mock everything — prefer integration tests when feasible
- Ignore flaky tests — fix or quarantine them
- Use `()` tests — channels, barriers, or `tokio::time::()`
- Skip error path testing
## CI Integration
```yaml
# GitHub Actions
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- name: Check formatting
run: cargo fmt --check
- name: Clippy
run: cargo clippy -- -D warnings
- name: Run tests
run: cargo test
- uses: taiki-e/install-action@cargo-llvm-cov
- name: Coverage
run: cargo llvm-cov --fail-under-lines
Remember: Tests are documentation. They show how your code is meant to be used. Write them clearly and keep them up to date.
Source: bl1nk-bot/bl1nk-agents-manager — distributed by TomeVault.