home / skills / autumnsgrove / groveengine / rust-testing

rust-testing skill

/.claude/skills/rust-testing

This skill helps you write, run, and debug Rust tests across unit, integration, doc, and property-based setups.

npx playbooks add skill autumnsgrove/groveengine --skill rust-testing

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.3 KB
---
name: rust-testing
description: Write and run Rust tests using cargo test with unit tests, integration tests, doc tests, and property-based testing. Use when writing Rust tests or setting up test infrastructure.
---

# Rust Testing Skill

## When to Activate

Activate this skill when:
- Writing Rust unit tests
- Creating integration tests
- Working with doc tests
- Setting up property-based testing
- Running benchmarks

## Quick Commands

```bash
# Run all tests
cargo test

# With output
cargo test -- --nocapture

# Run specific test
cargo test test_user_create

# Run tests in module
cargo test auth::

# Run ignored tests
cargo test -- --ignored

# Doc tests only
cargo test --doc

# Integration tests only
cargo test --test integration
```

## Unit Tests (Same File)

```rust
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
    }

    #[test]
    fn test_add_negative() {
        assert_eq!(add(-1, -1), -2);
    }
}
```

## Test Attributes

```rust
#[test]
fn regular_test() { }

#[test]
#[ignore]
fn slow_test() { }  // Skip unless --ignored

#[test]
#[should_panic]
fn test_panic() {
    panic!("This should panic");
}

#[test]
#[should_panic(expected = "specific message")]
fn test_panic_message() {
    panic!("specific message here");
}

#[test]
fn test_with_result() -> Result<(), String> {
    let result = some_operation()?;
    assert_eq!(result, expected);
    Ok(())
}
```

## Assertions

```rust
// Basic
assert_eq!(1 + 1, 2);
assert_ne!(1 + 1, 3);
assert!(true);

// With messages
assert_eq!(result, expected, "values should match: got {}", result);

// Pattern matching
assert!(matches!(value, Pattern::Variant(_)));

// Option/Result
assert!(some_option.is_some());
assert!(some_result.is_ok());
```

## Integration Tests

```rust
// tests/api_integration.rs
use my_crate::{Config, Server};

#[test]
fn test_server_startup() {
    let config = Config::default();
    let server = Server::new(config);
    assert!(server.start().is_ok());
}
```

## Directory Structure

```
project/
├── Cargo.toml
├── src/
│   ├── lib.rs          # Unit tests in #[cfg(test)]
│   └── user.rs         # Module with inline tests
└── tests/              # Integration tests
    ├── common/
    │   └── mod.rs      # Shared utilities
    └── api_test.rs
```

## Mocking with Traits

```rust
pub trait UserRepository {
    fn find_by_id(&self, id: u64) -> Option<User>;
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;

    struct MockUserRepo {
        users: HashMap<u64, User>,
    }

    impl UserRepository for MockUserRepo {
        fn find_by_id(&self, id: u64) -> Option<User> {
            self.users.get(&id).cloned()
        }
    }

    #[test]
    fn test_user_service() {
        let mut users = HashMap::new();
        users.insert(1, User { id: 1, email: "[email protected]".into() });
        let repo = MockUserRepo { users };

        let service = UserService::new(Box::new(repo));
        let user = service.get_user(1).unwrap();
        assert_eq!(user.email, "[email protected]");
    }
}
```

## Async Testing (tokio)

```rust
#[tokio::test]
async fn test_async_operation() {
    let result = fetch_data().await;
    assert!(result.is_ok());
}

#[tokio::test]
async fn test_with_timeout() {
    let result = tokio::time::timeout(
        Duration::from_secs(5),
        slow_operation()
    ).await;
    assert!(result.is_ok());
}
```

## Doc Tests

```rust
/// Adds two numbers together.
///
/// # Examples
///
/// ```
/// use my_crate::add;
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}
```

## Property-Based Testing (proptest)

```rust
use proptest::prelude::*;

proptest! {
    #[test]
    fn test_add_commutative(a: i32, b: i32) {
        prop_assert_eq!(add(a, b), add(b, a));
    }
}
```

## Coverage

```bash
# Using cargo-tarpaulin
cargo install cargo-tarpaulin
cargo tarpaulin --out Html

# Using cargo-llvm-cov
cargo install cargo-llvm-cov
cargo llvm-cov --html
```

## Related Resources

See `AgentUsage/testing_rust.md` for complete documentation including:
- Benchmarking with criterion
- Setup/teardown patterns
- Mockall crate usage
- CI configuration

Overview

This skill helps you write and run Rust tests using cargo test across unit tests, integration tests, doc tests, async tests, and property-based tests. It provides practical commands, test patterns, and examples to set up reliable test suites for a multi-tenant blog platform or any Rust project. Use it to improve test coverage, speed up feedback loops, and integrate testing into CI pipelines.

How this skill works

The skill outlines common cargo test invocations and flags, demonstrates inline unit tests and separate integration tests, and shows patterns for async tests (tokio), trait-based mocking, doc tests, and property-based testing with proptest. It also covers test attributes like ignore and should_panic, result-returning tests, and tools for coverage reporting such as cargo-tarpaulin and cargo-llvm-cov.

When to use it

  • When adding unit tests directly in modules using #[cfg(test)]
  • When writing integration tests in the tests/ directory to exercise public API
  • When documenting examples that should be validated via doc tests
  • When testing async code with tokio or adding timeouts
  • When introducing property-based tests to catch edge cases automatically

Best practices

  • Keep unit tests small and focused; test one behavior per test
  • Use integration tests to cover high-level flows and external-facing APIs
  • Share test utilities in tests/common to avoid duplication
  • Mock external dependencies via traits to keep tests deterministic
  • Mark long-running tests with #[ignore] and run them separately in CI or locally

Example use cases

  • Verify core business logic like user creation, authentication, and permissions with unit tests
  • Test HTTP endpoints and server startup behavior with integration tests under tests/
  • Validate library examples and expected behavior using doc tests in public APIs
  • Run async database or network interactions with #[tokio::test] and timeouts
  • Find edge-case failures using proptest to assert properties such as commutativity

FAQ

How do I run a single test or group of tests?

Use cargo test <name> for a specific test, or cargo test module::prefix to run tests in a module. Add -- --nocapture to see stdout from tests.

How can I include async tests and timeouts?

Use #[tokio::test] for async tests. Wrap long operations with tokio::time::timeout(Duration::from_secs(n), async_op()).await and assert the result.