๐Ÿฆ€ Testing Frameworks in TuskLang with Rust

Rust Documentation

Testing Frameworks in TuskLang with Rust

๐Ÿงช Testing Foundation

Testing frameworks with TuskLang and Rust provide comprehensive tools for building reliable applications. This guide covers unit testing, integration testing, property-based testing, and advanced testing strategies.

๐Ÿ—๏ธ Testing Architecture

Testing Stack

[testing_architecture]
unit_testing: true
integration_testing: true
property_testing: true
mocking: true

[testing_components] tokio_test: "async_testing" mockall: "mocking_framework" proptest: "property_testing" criterion: "benchmarking"

Testing Configuration

[testing_configuration]
enable_unit_tests: true
enable_integration_tests: true
enable_property_tests: true
enable_benchmarks: true

[testing_implementation] use tokio_test; use mockall::{automock, predicate::*}; use proptest::prelude::*; use criterion::{black_box, criterion_group, criterion_main, Criterion};

// Test manager pub struct TestManager { config: TestConfig, test_results: Arc<RwLock<Vec<TestResult>>>, coverage_data: Arc<RwLock<CoverageData>>, }

#[derive(Debug, Clone)] pub struct TestConfig { pub parallel_tests: bool, pub test_timeout: Duration, pub enable_coverage: bool, pub coverage_threshold: f64, pub enable_fuzzing: bool, pub fuzz_iterations: u32, }

#[derive(Debug, Clone)] pub struct TestResult { pub test_name: String, pub test_type: TestType, pub status: TestStatus, pub duration: Duration, pub error_message: Option<String>, pub coverage_percentage: Option<f64>, }

#[derive(Debug, Clone)] pub enum TestType { Unit, Integration, Property, Benchmark, Fuzz, }

#[derive(Debug, Clone)] pub enum TestStatus { Passed, Failed, Skipped, Timeout, }

#[derive(Debug, Clone)] pub struct CoverageData { pub total_lines: u64, pub covered_lines: u64, pub total_functions: u64, pub covered_functions: u64, pub total_branches: u64, pub covered_branches: u64, }

impl TestManager { pub fn new(config: TestConfig) -> Self { Self { config, test_results: Arc::new(RwLock::new(Vec::new())), coverage_data: Arc::new(RwLock::new(CoverageData { total_lines: 0, covered_lines: 0, total_functions: 0, covered_functions: 0, total_branches: 0, covered_branches: 0, })), } } pub async fn run_all_tests(&self) -> TestSuiteResult { let start_time = Instant::now(); let mut results = Vec::new(); // Run unit tests results.extend(self.run_unit_tests().await); // Run integration tests results.extend(self.run_integration_tests().await); // Run property tests results.extend(self.run_property_tests().await); // Run benchmarks if self.config.enable_fuzzing { results.extend(self.run_fuzz_tests().await); } let total_duration = start_time.elapsed(); let passed_tests = results.iter().filter(|r| matches!(r.status, TestStatus::Passed)).count(); let failed_tests = results.iter().filter(|r| matches!(r.status, TestStatus::Failed)).count(); // Store results { let mut test_results = self.test_results.write().await; test_results.extend(results.clone()); } TestSuiteResult { total_tests: results.len(), passed_tests, failed_tests, total_duration, results, } } async fn run_unit_tests(&self) -> Vec<TestResult> { let mut results = Vec::new(); // Example unit tests results.push(self.run_test("test_addition", TestType::Unit, || { assert_eq!(2 + 2, 4); Ok(()) }).await); results.push(self.run_test("test_string_operations", TestType::Unit, || { let s = "hello".to_string(); assert_eq!(s.len(), 5); assert_eq!(s.to_uppercase(), "HELLO"); Ok(()) }).await); results } async fn run_integration_tests(&self) -> Vec<TestResult> { let mut results = Vec::new(); // Example integration tests results.push(self.run_test("test_database_connection", TestType::Integration, || { // Simulate database connection test tokio::time::sleep(Duration::from_millis(100)).await; Ok(()) }).await); results.push(self.run_test("test_api_endpoint", TestType::Integration, || { // Simulate API endpoint test tokio::time::sleep(Duration::from_millis(200)).await; Ok(()) }).await); results } async fn run_property_tests(&self) -> Vec<TestResult> { let mut results = Vec::new(); // Example property tests results.push(self.run_property_test("test_addition_commutative", || { proptest!(|(a: i32, b: i32)| { assert_eq!(a + b, b + a); }); Ok(()) }).await); results.push(self.run_property_test("test_string_reverse", || { proptest!(|(s: String)| { let reversed = s.chars().rev().collect::<String>(); let double_reversed = reversed.chars().rev().collect::<String>(); assert_eq!(s, double_reversed); }); Ok(()) }).await); results } async fn run_fuzz_tests(&self) -> Vec<TestResult> { let mut results = Vec::new(); // Example fuzz tests for i in 0..self.config.fuzz_iterations { results.push(self.run_test(&format!("fuzz_test_{}", i), TestType::Fuzz, || { let input = rand::random::<u64>(); // Test with random input assert!(input >= 0); Ok(()) }).await); } results } async fn run_test<F, Fut>(&self, name: &str, test_type: TestType, test_fn: F) -> TestResult where F: FnOnce() -> Fut, Fut: Future<Output = Result<(), String>>, { let start_time = Instant::now(); let result = tokio::time::timeout(self.config.test_timeout, test_fn()).await; let duration = start_time.elapsed(); let (status, error_message) = match result { Ok(Ok(())) => (TestStatus::Passed, None), Ok(Err(e)) => (TestStatus::Failed, Some(e)), Err(_) => (TestStatus::Timeout, Some("Test timed out".to_string())), }; TestResult { test_name: name.to_string(), test_type, status, duration, error_message, coverage_percentage: None, // Would be calculated in real implementation } } async fn run_property_test<F>(&self, name: &str, test_fn: F) -> TestResult where F: FnOnce() -> Result<(), String>, { let start_time = Instant::now(); let result = test_fn(); let duration = start_time.elapsed(); let (status, error_message) = match result { Ok(()) => (TestStatus::Passed, None), Err(e) => (TestStatus::Failed, Some(e)), }; TestResult { test_name: name.to_string(), test_type: TestType::Property, status, duration, error_message, coverage_percentage: None, } } pub async fn get_test_results(&self) -> Vec<TestResult> { self.test_results.read().await.clone() } pub async fn get_coverage_data(&self) -> CoverageData { self.coverage_data.read().await.clone() } }

#[derive(Debug)] pub struct TestSuiteResult { pub total_tests: usize, pub passed_tests: usize, pub failed_tests: usize, pub total_duration: Duration, pub results: Vec<TestResult>, }

๐Ÿงช Unit Testing

Advanced Unit Testing

[unit_testing]
test_organization: true
test_fixtures: true
test_utilities: true

[unit_implementation] use std::sync::Arc;

// Test utilities pub struct TestUtils;

impl TestUtils { pub fn assert_approx_eq(a: f64, b: f64, epsilon: f64) { assert!((a - b).abs() < epsilon, "{} and {} are not approximately equal", a, b); } pub fn assert_vec_eq<T: PartialEq + std::fmt::Debug>(a: &[T], b: &[T]) { assert_eq!(a.len(), b.len(), "Vector lengths differ"); for (i, (a_val, b_val)) in a.iter().zip(b.iter()).enumerate() { assert_eq!(a_val, b_val, "Values at index {} differ: {:?} != {:?}", i, a_val, b_val); } } pub fn assert_result_ok<T, E: std::fmt::Debug>(result: Result<T, E>) -> T { match result { Ok(value) => value, Err(e) => panic!("Expected Ok, got Err: {:?}", e), } } pub fn assert_result_err<T: std::fmt::Debug, E: std::fmt::Debug>(result: Result<T, E>) -> E { match result { Ok(value) => panic!("Expected Err, got Ok: {:?}", value), Err(e) => e, } } }

// Test fixtures pub struct TestFixture { pub data: Vec<i32>, pub config: TestConfig, }

impl TestFixture { pub fn new() -> Self { Self { data: vec![1, 2, 3, 4, 5], config: TestConfig { parallel_tests: false, test_timeout: Duration::from_secs(30), enable_coverage: false, coverage_threshold: 80.0, enable_fuzzing: false, fuzz_iterations: 1000, }, } } pub fn with_data(mut self, data: Vec<i32>) -> Self { self.data = data; self } pub fn with_config(mut self, config: TestConfig) -> Self { self.config = config; self } pub fn setup(&mut self) { // Setup test environment println!("Setting up test fixture with {} items", self.data.len()); } pub fn teardown(&mut self) { // Cleanup test environment println!("Tearing down test fixture"); } }

// Example functions to test pub struct Calculator;

impl Calculator { pub fn add(a: i32, b: i32) -> i32 { a + b } pub fn subtract(a: i32, b: i32) -> i32 { a - b } pub fn multiply(a: i32, b: i32) -> i32 { a * b } pub fn divide(a: i32, b: i32) -> Result<i32, String> { if b == 0 { Err("Division by zero".to_string()) } else { Ok(a / b) } } pub fn power(base: i32, exponent: u32) -> i32 { base.pow(exponent) } }

// Unit tests #[cfg(test)] mod unit_tests { use super::*; #[test] fn test_calculator_add() { assert_eq!(Calculator::add(2, 3), 5); assert_eq!(Calculator::add(-1, 1), 0); assert_eq!(Calculator::add(0, 0), 0); } #[test] fn test_calculator_subtract() { assert_eq!(Calculator::subtract(5, 3), 2); assert_eq!(Calculator::subtract(1, 1), 0); assert_eq!(Calculator::subtract(0, 5), -5); } #[test] fn test_calculator_multiply() { assert_eq!(Calculator::multiply(2, 3), 6); assert_eq!(Calculator::multiply(-2, 3), -6); assert_eq!(Calculator::multiply(0, 5), 0); } #[test] fn test_calculator_divide() { assert_eq!(Calculator::divide(6, 2), Ok(3)); assert_eq!(Calculator::divide(5, 2), Ok(2)); assert_eq!(Calculator::divide(0, 5), Ok(0)); // Test division by zero assert!(Calculator::divide(5, 0).is_err()); } #[test] fn test_calculator_power() { assert_eq!(Calculator::power(2, 3), 8); assert_eq!(Calculator::power(5, 0), 1); assert_eq!(Calculator::power(0, 5), 0); } #[test] fn test_utils_assert_approx_eq() { TestUtils::assert_approx_eq(3.14159, 3.14159, 0.00001); TestUtils::assert_approx_eq(1.0, 1.0000001, 0.001); } #[test] fn test_utils_assert_vec_eq() { let a = vec![1, 2, 3]; let b = vec![1, 2, 3]; TestUtils::assert_vec_eq(&a, &b); } #[test] fn test_utils_assert_result_ok() { let result: Result<i32, String> = Ok(42); let value = TestUtils::assert_result_ok(result); assert_eq!(value, 42); } #[test] #[should_panic(expected = "Expected Ok, got Err")] fn test_utils_assert_result_ok_panics() { let result: Result<i32, String> = Err("error".to_string()); TestUtils::assert_result_ok(result); } #[test] fn test_utils_assert_result_err() { let result: Result<i32, String> = Err("error".to_string()); let error = TestUtils::assert_result_err(result); assert_eq!(error, "error"); } #[test] #[should_panic(expected = "Expected Err, got Ok")] fn test_utils_assert_result_err_panics() { let result: Result<i32, String> = Ok(42); TestUtils::assert_result_err(result); } }

๐Ÿ”— Integration Testing

Advanced Integration Testing

[integration_testing]
test_services: true
test_databases: true
test_apis: true

[integration_implementation] use tokio::net::TcpListener; use hyper::{Body, Request, Response, Server}; use hyper::service::{make_service_fn, service_fn};

// Integration test utilities pub struct IntegrationTestUtils;

impl IntegrationTestUtils { pub async fn start_test_server() -> String { let addr = ([127, 0, 0, 1], 0).into(); let listener = TcpListener::bind(addr).await.unwrap(); let addr = listener.local_addr().unwrap(); let make_svc = make_service_fn(|_conn| async { Ok::<_, hyper::Error>(service_fn(|req: Request<Body>| async { let response = match req.uri().path() { "/health" => Response::new(Body::from("OK")), "/api/test" => Response::new(Body::from(r#"{"status":"success"}"#)), _ => Response::builder() .status(404) .body(Body::from("Not Found")) .unwrap(), }; Ok::<_, hyper::Error>(response) })) }); let server = Server::from_tcp(listener).unwrap().serve(make_svc); tokio::spawn(async move { server.await.unwrap(); }); format!("http://{}", addr) } pub async fn wait_for_server(url: &str) -> Result<(), Box<dyn std::error::Error>> { let client = reqwest::Client::new(); let mut attempts = 0; let max_attempts = 10; while attempts < max_attempts { match client.get(&format!("{}/health", url)).send().await { Ok(response) => { if response.status().is_success() { return Ok(()); } } Err(_) => {} } tokio::time::sleep(Duration::from_millis(100)).await; attempts += 1; } Err("Server failed to start".into()) } pub async fn cleanup_test_data() { // Cleanup test data println!("Cleaning up test data"); } }

// Mock database for integration tests pub struct MockDatabase { data: Arc<RwLock<HashMap<String, String>>>, }

impl MockDatabase { pub fn new() -> Self { Self { data: Arc::new(RwLock::new(HashMap::new())), } } pub async fn insert(&self, key: String, value: String) -> Result<(), String> { let mut data = self.data.write().await; data.insert(key, value); Ok(()) } pub async fn get(&self, key: &str) -> Result<Option<String>, String> { let data = self.data.read().await; Ok(data.get(key).cloned()) } pub async fn delete(&self, key: &str) -> Result<(), String> { let mut data = self.data.write().await; data.remove(key); Ok(()) } pub async fn clear(&self) { let mut data = self.data.write().await; data.clear(); } }

// Integration tests #[cfg(test)] mod integration_tests { use super::*; #[tokio::test] async fn test_server_health_endpoint() { let server_url = IntegrationTestUtils::start_test_server().await; IntegrationTestUtils::wait_for_server(&server_url).await.unwrap(); let client = reqwest::Client::new(); let response = client.get(&format!("{}/health", server_url)) .send() .await .unwrap(); assert!(response.status().is_success()); assert_eq!(response.text().await.unwrap(), "OK"); } #[tokio::test] async fn test_api_endpoint() { let server_url = IntegrationTestUtils::start_test_server().await; IntegrationTestUtils::wait_for_server(&server_url).await.unwrap(); let client = reqwest::Client::new(); let response = client.get(&format!("{}/api/test", server_url)) .send() .await .unwrap(); assert!(response.status().is_success()); let body: serde_json::Value = response.json().await.unwrap(); assert_eq!(body["status"], "success"); } #[tokio::test] async fn test_database_operations() { let db = MockDatabase::new(); // Test insert db.insert("key1".to_string(), "value1".to_string()).await.unwrap(); // Test get let value = db.get("key1").await.unwrap(); assert_eq!(value, Some("value1".to_string())); // Test delete db.delete("key1").await.unwrap(); let value = db.get("key1").await.unwrap(); assert_eq!(value, None); // Cleanup db.clear().await; } #[tokio::test] async fn test_calculator_integration() { // Test calculator with database storage let db = MockDatabase::new(); let result = Calculator::add(5, 3); db.insert("last_result".to_string(), result.to_string()).await.unwrap(); let stored_result = db.get("last_result").await.unwrap(); assert_eq!(stored_result, Some("8".to_string())); db.clear().await; } }

๐Ÿ” Property-Based Testing

Advanced Property Testing

[property_testing]
proptest_framework: true
custom_strategies: true
shrinking: true

[property_implementation] use proptest::prelude::*; use proptest::test_runner::TestCaseError;

// Property test utilities pub struct PropertyTestUtils;

impl PropertyTestUtils { pub fn test_addition_properties() { proptest!(|(a: i32, b: i32)| { // Commutative property assert_eq!(Calculator::add(a, b), Calculator::add(b, a)); // Associative property let c = 42i32; assert_eq!( Calculator::add(Calculator::add(a, b), c), Calculator::add(a, Calculator::add(b, c)) ); // Identity property assert_eq!(Calculator::add(a, 0), a); }); } pub fn test_multiplication_properties() { proptest!(|(a: i32, b: i32)| { // Commutative property assert_eq!(Calculator::multiply(a, b), Calculator::multiply(b, a)); // Associative property let c = 42i32; assert_eq!( Calculator::multiply(Calculator::multiply(a, b), c), Calculator::multiply(a, Calculator::multiply(b, c)) ); // Identity property assert_eq!(Calculator::multiply(a, 1), a); // Zero property assert_eq!(Calculator::multiply(a, 0), 0); }); } pub fn test_string_properties() { proptest!(|(s: String)| { // Reverse twice should be identity let reversed = s.chars().rev().collect::<String>(); let double_reversed = reversed.chars().rev().collect::<String>(); assert_eq!(s, double_reversed); // Length should be preserved assert_eq!(s.len(), reversed.len()); // Empty string properties if s.is_empty() { assert_eq!(reversed, ""); } }); } pub fn test_vector_properties() { proptest!(|(v: Vec<i32>)| { // Reverse twice should be identity let mut reversed = v.clone(); reversed.reverse(); let mut double_reversed = reversed.clone(); double_reversed.reverse(); assert_eq!(v, double_reversed); // Length should be preserved assert_eq!(v.len(), reversed.len()); // Sum should be preserved let original_sum: i32 = v.iter().sum(); let reversed_sum: i32 = reversed.iter().sum(); assert_eq!(original_sum, reversed_sum); }); } }

// Custom strategies pub fn custom_string_strategy() -> impl Strategy<Value = String> { prop::collection::vec(prop::char::any(), 0..100) .prop_map(|chars| chars.into_iter().collect()) }

pub fn custom_number_strategy() -> impl Strategy<Value = i32> { prop::num::i32::ANY .prop_filter("Non-zero numbers", |&x| x != 0) }

// Property tests #[cfg(test)] mod property_tests { use super::*; proptest! { #[test] fn test_addition_commutative(a: i32, b: i32) { assert_eq!(Calculator::add(a, b), Calculator::add(b, a)); } #[test] fn test_addition_associative(a: i32, b: i32) { let c = 42i32; assert_eq!( Calculator::add(Calculator::add(a, b), c), Calculator::add(a, Calculator::add(b, c)) ); } #[test] fn test_multiplication_commutative(a: i32, b: i32) { assert_eq!(Calculator::multiply(a, b), Calculator::multiply(b, a)); } #[test] fn test_multiplication_zero(a: i32) { assert_eq!(Calculator::multiply(a, 0), 0); } #[test] fn test_division_by_non_zero(a: i32, b in custom_number_strategy()) { let result = Calculator::divide(a, b); assert!(result.is_ok()); } #[test] fn test_string_reverse_identity(s in custom_string_strategy()) { let reversed = s.chars().rev().collect::<String>(); let double_reversed = reversed.chars().rev().collect::<String>(); assert_eq!(s, double_reversed); } #[test] fn test_vector_reverse_identity(v: Vec<i32>) { let mut reversed = v.clone(); reversed.reverse(); let mut double_reversed = reversed.clone(); double_reversed.reverse(); assert_eq!(v, double_reversed); } } }

๐ŸŽฏ Best Practices

1. Test Organization

- Organize tests by functionality and type - Use descriptive test names and clear assertions - Implement test fixtures for common setup - Use test utilities for common operations

2. Test Coverage

- Aim for high test coverage (80%+) - Test edge cases and error conditions - Use property-based testing for complex logic - Implement integration tests for critical paths

3. Test Performance

- Keep tests fast and independent - Use mocking for external dependencies - Implement parallel test execution - Use benchmarks for performance-critical code

4. Test Maintenance

- Keep tests up to date with code changes - Use test data factories for complex objects - Implement test utilities for common patterns - Document test requirements and assumptions

5. TuskLang Integration

- Use TuskLang configuration for test parameters - Implement test reporting with TuskLang - Configure test environments through TuskLang - Use TuskLang for test data management

Testing frameworks with TuskLang and Rust provide comprehensive tools for building reliable applications with robust testing strategies and coverage analysis.