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.
1.0.0Add 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
Implement test module structure using `#[cfg(test)]` and nested modules.
Write unit, integration, documentation, and property tests.
Apply advanced assertion patterns for complex scenarios.
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
testsmodules 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.