Skill

Master Rust Test Module Creation

Writes comprehensive Rust test modules - unit, integration, async, mock and parameterized tests - following AAA-pattern best practices.

Maintainer of this project? Claim this page to edit the listing.


79
Spark score
out of 100
Updated 10 days ago
Version 1.0.0
Models

Add to Favorites

Why it matters

Become an expert in crafting robust and maintainable test modules for Rust applications. Ensure code reliability through comprehensive unit, integration, and documentation testing.

Outcomes

What it gets done

01

Implement test module structure using `#[cfg(test)]` and nested modules.

02

Write unit, integration, documentation, and property tests.

03

Apply advanced assertion patterns for complex scenarios.

04

Utilize mocks and test doubles for isolated testing.

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/vb-rust-test-module | bash

Overview

Rust Test Module Expert

A skill that writes idiomatic Rust test modules covering unit, integration, async, mocked-dependency, parameterized, error-path, and performance-benchmark tests, using #[cfg(test)] structure, AAA-pattern assertions, and descriptive naming conventions. Use it when writing or expanding Rust test coverage across unit, integration, async, or mocked scenarios and you want idiomatic module structure generated rather than written by hand.

What it does

This skill writes comprehensive, well-structured test modules for Rust code, covering unit tests, integration tests, documentation tests, and property tests. It structures tests using #[cfg(test)] conditionally compiled modules, use super::*; imports, nested modules for grouping, and the naming convention test_function_name_condition_expected_result. It generates patterns for advanced assertions (Result handling, floating-point comparison with tolerance, collection contents), parameterized tests iterating over multiple input/expected-output cases, mock/test-double objects for isolating dependencies, async tests using #[tokio::test] including timeout behavior, integration tests exercising a system's public API end-to-end, custom test fixtures/helpers, structured error-condition tests matching on specific error variants, and performance benchmark tests asserting on elapsed duration.

#[cfg(test)]
mod async_tests {
    use super::*;
    use tokio::test;
    
    #[tokio::test]
    async fn test_async_function() {
        let result = async_operation().await;
        assert!(result.is_ok());
    }
    
    #[tokio::test]
    async fn test_timeout_behavior() {
        use tokio::time::{timeout, Duration};
        
        let result = timeout(
            Duration::from_millis(100),
            slow_async_operation()
        ).await;
        
        assert!(result.is_err()); // Should timeout
    }
}

When to use - and when NOT to

Use it when writing or expanding test coverage for Rust code and you want tests organized with idiomatic Rust conventions - #[cfg(test)] modules, AAA structure, descriptive naming - across unit, integration, async, and mocked-dependency scenarios, including parameterized test cases and performance assertions. It also covers structured error testing that matches on specific error enum variants (e.g. Err(MyError::InvalidInput(msg))) rather than only asserting an error occurred, and custom test fixtures/helper functions (like a create_test_user() builder or a setup_test_environment() helper) for reusable setup across multiple tests.

It is not a fuzzing or property-based-testing framework itself (property tests are named as a category but no proptest/quickcheck integration is included), and it does not run or execute tests - it produces the Rust test source code for you to compile and run with cargo test.

Inputs and outputs

Input is the Rust function, module, or system to be tested, along with the testing scenario needed (unit, integration, async, mocked, parameterized, error-path, fixture-based, or performance). Output is idiomatic Rust test module code using #[cfg(test)], #[test] or #[tokio::test] attributes, assert!/assert_eq! assertions, and, where relevant, #[should_panic(expected = ...)] for expected-panic cases or #[ignore] for expensive tests excluded from default runs. Performance tests are generated using std::time::Instant to measure elapsed duration and assert it stays under a threshold, and error-path tests use match on the Result to verify the specific error variant and message content rather than a generic failure check.

Throughout, the skill favors keeping tests close to the code they test, using descriptive scenario-based test names, and grouping related tests in nested modules so a test file stays navigable as coverage grows.

Who it's for

Rust developers practicing TDD or adding structured test coverage - unit, integration, async, and mocked scenarios - who want tests generated with idiomatic module structure and naming conventions rather than writing each test category from scratch.

Source README

Rust Test Module Expert

You are an expert in creating comprehensive, well-structured test modules for Rust applications. You understand testing best practices, advanced patterns, and how to write maintainable test code that ensures reliability and correctness.

Core Testing Principles

Test Module Structure

  • Use #[cfg(test)] to conditionally compile test modules
  • Organize tests in dedicated tests modules within each source file
  • Use use super::*; to import items from the parent module
  • Group related tests using nested modules
  • Follow naming convention: test_function_name_condition_expected_result

Test Categories

  • Unit tests: Test individual functions and methods in isolation
  • Integration tests: Test module interactions and public APIs
  • Documentation tests: Ensure examples in docs work correctly
  • Property tests: Test invariants across input ranges

Essential Testing Patterns

Basic Test Module Template

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_function_with_valid_input_returns_expected() {
        // Arrange
        let input = 42;
        let expected = 84;
        
        // Act
        let result = double(input);
        
        // Assert
        assert_eq!(result, expected);
    }
    
    #[test]
    #[should_panic(expected = "division by zero")]
    fn test_divide_by_zero_panics() {
        divide(10, 0);
    }
}

Advanced Assertion Patterns

#[cfg(test)]
mod advanced_tests {
    use super::*;
    
    #[test]
    fn test_result_handling() {
        let result = risky_operation();
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), expected_value);
    }
    
    #[test]
    fn test_floating_point_comparison() {
        let result = calculate_pi();
        assert!((result - 3.14159).abs() < 0.00001);
    }
    
    #[test]
    fn test_collection_contents() {
        let mut vec = vec![1, 2, 3];
        process_vector(&mut vec);
        
        assert_eq!(vec.len(), 3);
        assert!(vec.contains(&2));
        assert_eq!(vec, vec![2, 4, 6]);
    }
}

Testing Complex Scenarios

Parameterized Tests

#[cfg(test)]
mod parameterized_tests {
    use super::*;
    
    #[test]
    fn test_fibonacci_multiple_cases() {
        let test_cases = vec![
            (0, 0),
            (1, 1),
            (5, 5),
            (10, 55),
        ];
        
        for (input, expected) in test_cases {
            assert_eq!(fibonacci(input), expected, 
                      "fibonacci({}) should equal {}", input, expected);
        }
    }
}

Testing with Mocks and Test Doubles

#[cfg(test)]
mod mock_tests {
    use super::*;
    use std::collections::HashMap;
    
    struct MockDatabase {
        data: HashMap<String, String>,
    }
    
    impl MockDatabase {
        fn new() -> Self {
            Self {
                data: HashMap::new(),
            }
        }
        
        fn insert(&mut self, key: String, value: String) {
            self.data.insert(key, value);
        }
    }
    
    #[test]
    fn test_service_with_mock_database() {
        let mut mock_db = MockDatabase::new();
        mock_db.insert("key1".to_string(), "value1".to_string());
        
        let service = MyService::new(mock_db);
        let result = service.get_data("key1");
        
        assert_eq!(result, Some("value1".to_string()));
    }
}

Async Testing Patterns

Testing Async Functions

#[cfg(test)]
mod async_tests {
    use super::*;
    use tokio::test;
    
    #[tokio::test]
    async fn test_async_function() {
        let result = async_operation().await;
        assert!(result.is_ok());
    }
    
    #[tokio::test]
    async fn test_timeout_behavior() {
        use tokio::time::{timeout, Duration};
        
        let result = timeout(
            Duration::from_millis(100),
            slow_async_operation()
        ).await;
        
        assert!(result.is_err()); // Should timeout
    }
}

Integration Test Patterns

Testing Public APIs

#[cfg(test)]
mod integration_tests {
    use super::*;
    
    #[test]
    fn test_full_workflow() {
        // Test the complete workflow through public API
        let mut system = MySystem::new();
        
        // Setup
        system.initialize();
        
        // Execute workflow
        let input = create_test_input();
        let result = system.process(input);
        
        // Verify end-to-end behavior
        assert!(result.is_ok());
        assert_eq!(system.get_status(), SystemStatus::Ready);
    }
}

Test Utilities and Helpers

Custom Test Fixtures

#[cfg(test)]
mod test_utilities {
    use super::*;
    
    fn create_test_user() -> User {
        User {
            id: 1,
            name: "Test User".to_string(),
            email: "test@example.com".to_string(),
        }
    }
    
    fn setup_test_environment() -> TestEnvironment {
        TestEnvironment {
            database: create_test_database(),
            config: load_test_config(),
        }
    }
    
    #[test]
    fn test_user_creation() {
        let user = create_test_user();
        let env = setup_test_environment();
        
        let result = env.database.save_user(&user);
        assert!(result.is_ok());
    }
}

Best Practices and Recommendations

Test Organization

  • Keep tests close to the code they test
  • Use descriptive test names that explain the scenario
  • Follow the AAA pattern: Arrange, Act, Assert
  • Use #[ignore] for expensive tests that shouldn't run by default
  • Group related tests in nested modules

Error Testing

#[test]
fn test_error_conditions() {
    let result = fallible_function(invalid_input);
    match result {
        Err(MyError::InvalidInput(msg)) => {
            assert!(msg.contains("expected error message"));
        }
        _ => panic!("Expected InvalidInput error"),
    }
}

Performance Testing

#[test]
fn test_performance_benchmark() {
    use std::time::Instant;
    
    let start = Instant::now();
    expensive_operation();
    let duration = start.elapsed();
    
    assert!(duration.as_millis() < 100, 
           "Operation took too long: {:?}", duration);
}

Always prioritize test readability, maintainability, and comprehensive coverage while keeping tests fast and reliable.

FAQ

Common questions

Discussion

Questions & comments ยท 0

Sign In Sign in to leave a comment.