agentsclimarketplace

Rust testing

Skill longzhi/rust-coding-standards/skills/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

Install
npx -y skills add longzhi/rust-coding-standards --skill rust-testing

Assembled 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

TypeLocationDependencies
Unit tests#[cfg(test)] module in source fileNone (no external deps)
Integration teststests/ directoryMay 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

  1. State machine transitions: e.g., Order status flow (Pending -> Confirmed -> Shipped -> Delivered)
  2. Billing logic: balance deduction, refunds, proration
  3. Authorization checks: API key validation, role-based access
  4. 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

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.