// rust/01-unit-testing.rs/*Unit testing patterns and best practices for Rust*/usestd::collections::HashMap;useanyhow::Result;usethiserror::Error;#[derive(Error, Debug)]pubenumCalculatorError{#[error("Division by zero")]DivisionByZero,#[error("Invalid operation: {0}")]InvalidOperation(String),}pubstructCalculator{history:Vec<String>,}implCalculator{pubfnnew()->Self{Self{history:Vec::new(),}}pubfnadd(&mutself,a:f64,b:f64)->f64{letresult=a+b;self.history.push(format!("{} + {} = {}",a,b,result));result}pubfnsubtract(&mutself,a:f64,b:f64)->f64{letresult=a-b;self.history.push(format!("{} - {} = {}",a,b,result));result}pubfnmultiply(&mutself,a:f64,b:f64)->f64{letresult=a*b;self.history.push(format!("{} * {} = {}",a,b,result));result}pubfndivide(&mutself,a:f64,b:f64)->Result<f64,CalculatorError>{ifb==0.0{returnErr(CalculatorError::DivisionByZero);}letresult=a/b;self.history.push(format!("{} / {} = {}",a,b,result));Ok(result)}pubfnget_history(&self)->&[String]{&self.history}pubfnclear_history(&mutself){self.history.clear();}}#[cfg(test)]modtests{usesuper::*;#[test]fntest_addition(){letmutcalc=Calculator::new();assert_eq!(calc.add(2.0,3.0),5.0);assert_eq!(calc.add(-1.0,1.0),0.0);assert_eq!(calc.add(0.0,0.0),0.0);}#[test]fntest_subtraction(){letmutcalc=Calculator::new();assert_eq!(calc.subtract(5.0,3.0),2.0);assert_eq!(calc.subtract(1.0,1.0),0.0);assert_eq!(calc.subtract(0.0,5.0),-5.0);}#[test]fntest_multiplication(){letmutcalc=Calculator::new();assert_eq!(calc.multiply(2.0,3.0),6.0);assert_eq!(calc.multiply(-2.0,3.0),-6.0);assert_eq!(calc.multiply(0.0,5.0),0.0);}#[test]fntest_division_success(){letmutcalc=Calculator::new();assert_eq!(calc.divide(6.0,2.0).unwrap(),3.0);assert_eq!(calc.divide(5.0,2.0).unwrap(),2.5);assert_eq!(calc.divide(0.0,5.0).unwrap(),0.0);}#[test]fntest_division_by_zero(){letmutcalc=Calculator::new();letresult=calc.divide(5.0,0.0);assert!(result.is_err());matchresult.unwrap_err(){CalculatorError::DivisionByZero=>{},_=>panic!("Expected DivisionByZero error"),}}#[test]fntest_history(){letmutcalc=Calculator::new();calc.add(1.0,2.0);calc.subtract(5.0,3.0);lethistory=calc.get_history();assert_eq!(history.len(),2);assert!(history[0].contains("1 + 2 = 3"));assert!(history[1].contains("5 - 3 = 2"));}#[test]fntest_clear_history(){letmutcalc=Calculator::new();calc.add(1.0,2.0);assert_eq!(calc.get_history().len(),1);calc.clear_history();assert_eq!(calc.get_history().len(),0);}#[test]fntest_floating_point_precision(){letmutcalc=Calculator::new();letresult=calc.add(0.1,0.2);assert!((result-0.3).abs()<1e-10);}}// Test with custom test harness#[cfg(test)]modcustom_tests{usesuper::*;// Test with setup and teardownfnsetup_calculator()->Calculator{letmutcalc=Calculator::new();calc.add(1.0,2.0);// Pre-populate with some datacalc}fnteardown_calculator(calc:&mutCalculator){calc.clear_history();}#[test]fntest_with_setup_teardown(){letmutcalc=setup_calculator();assert_eq!(calc.get_history().len(),1);calc.add(3.0,4.0);assert_eq!(calc.get_history().len(),2);teardown_calculator(&mutcalc);assert_eq!(calc.get_history().len(),0);}}
// rust/02-property-based-testing.rs/*Property-based testing patterns with proptest*/useproptest::prelude::*;usestd::collections::HashMap;pubstructStringProcessor{data:HashMap<String,String>,}implStringProcessor{pubfnnew()->Self{Self{data:HashMap::new(),}}pubfnadd(&mutself,key:String,value:String)->Result<(),String>{ifkey.is_empty(){returnErr("Key cannot be empty".to_string());}ifkey.len()>100{returnErr("Key too long".to_string());}ifvalue.len()>1000{returnErr("Value too long".to_string());}self.data.insert(key,value);Ok(())}pubfnget(&self,key:&str)->Option<&String>{self.data.get(key)}pubfnremove(&mutself,key:&str)->Option<String>{self.data.remove(key)}pubfnlen(&self)->usize{self.data.len()}pubfnis_empty(&self)->bool{self.data.is_empty()}pubfncontains_key(&self,key:&str)->bool{self.data.contains_key(key)}}#[cfg(test)]modproperty_tests{usesuper::*;proptest!{#[test]fntest_add_get_property(keyin"[a-zA-Z0-9_]{1,50}",valuein".*"){letmutprocessor=StringProcessor::new();processor.add(key.clone(),value.clone()).unwrap();assert_eq!(processor.get(&key),Some(&value));}#[test]fntest_add_remove_property(keyin"[a-zA-Z0-9_]{1,50}",valuein".*"){letmutprocessor=StringProcessor::new();processor.add(key.clone(),value.clone()).unwrap();assert_eq!(processor.remove(&key),Some(value));assert_eq!(processor.get(&key),None);}#[test]fntest_length_property(operationsinprop::collection::vec((any::<String>(),any::<String>()),0..100)){letmutprocessor=StringProcessor::new();letmutexpected_len=0;for(key,value)inoperations{if!key.is_empty()&&key.len()<=100&&value.len()<=1000{ifprocessor.add(key.clone(),value).is_ok(){expected_len+=1;}}}assert_eq!(processor.len(),expected_len);}#[test]fntest_empty_key_error(valuein".*"){letmutprocessor=StringProcessor::new();letresult=processor.add("".to_string(),value);assert!(result.is_err());assert!(result.unwrap_err().contains("empty"));}#[test]fntest_key_too_long(keyin"[a-zA-Z0-9_]{101,200}",valuein".*"){letmutprocessor=StringProcessor::new();letresult=processor.add(key,value);assert!(result.is_err());assert!(result.unwrap_err().contains("too long"));}#[test]fntest_value_too_long(keyin"[a-zA-Z0-9_]{1,50}",valuein"[a-zA-Z0-9_]{1001,2000}"){letmutprocessor=StringProcessor::new();letresult=processor.add(key,value);assert!(result.is_err());assert!(result.unwrap_err().contains("too long"));}}}// Custom property-based test strategies#[cfg(test)]modcustom_property_tests{usesuper::*;useproptest::strategy::{Strategy,ValueTree};useproptest::test_runner::TestRunner;// Custom strategy for generating valid keysfnvalid_key_strategy()->implStrategy<Value=String>{"[a-zA-Z0-9_]{1,50}".prop_map(|s|s)}// Custom strategy for generating valid valuesfnvalid_value_strategy()->implStrategy<Value=String>{".*".prop_filter("value length",|s|s.len()<=1000)}proptest!{#[test]fntest_custom_strategy(keyinvalid_key_strategy(),valueinvalid_value_strategy()){letmutprocessor=StringProcessor::new();assert!(processor.add(key.clone(),value.clone()).is_ok());assert_eq!(processor.get(&key),Some(&value));}}}
// rust/03-mock-testing.rs/*Mock testing patterns with mockall*/usemockall::mock;usestd::collections::HashMap;useanyhow::Result;// Trait to mockpubtraitDataRepository{fnget(&self,key:&str)->Option<String>;fnset(&mutself,key:String,value:String)->Result<()>;fndelete(&mutself,key:&str)->Result<()>;fnexists(&self,key:&str)->bool;}// ImplementationpubstructHashMapRepository{data:HashMap<String,String>,}implHashMapRepository{pubfnnew()->Self{Self{data:HashMap::new(),}}}implDataRepositoryforHashMapRepository{fnget(&self,key:&str)->Option<String>{self.data.get(key).cloned()}fnset(&mutself,key:String,value:String)->Result<()>{self.data.insert(key,value);Ok(())}fndelete(&mutself,key:&str)->Result<()>{self.data.remove(key);Ok(())}fnexists(&self,key:&str)->bool{self.data.contains_key(key)}}// Service that uses the repositorypubstructDataService{repository:Box<dynDataRepository>,}implDataService{pubfnnew(repository:Box<dynDataRepository>)->Self{Self{repository}}pubfnget_data(&self,key:&str)->Option<String>{self.repository.get(key)}pubfnset_data(&mutself,key:String,value:String)->Result<()>{self.repository.set(key,value)}pubfndelete_data(&mutself,key:&str)->Result<()>{self.repository.delete(key)}pubfndata_exists(&self,key:&str)->bool{self.repository.exists(key)}pubfnget_or_set_default(&mutself,key:String,default_value:String)->Result<String>{ifletSome(value)=self.repository.get(&key){Ok(value)}else{self.repository.set(key.clone(),default_value.clone())?;Ok(default_value)}}}// Mock the traitmock!{pubDataRepository{}implDataRepositoryforDataRepository{fnget(&self,key:&str)->Option<String>;fnset(&mutself,key:String,value:String)->Result<()>;fndelete(&mutself,key:&str)->Result<()>;fnexists(&self,key:&str)->bool;}}#[cfg(test)]modmock_tests{usesuper::*;usemockall::predicate::*;#[test]fntest_get_data_success(){letmutmock_repo=MockDataRepository::new();mock_repo.expect_get().with(eq("test_key")).times(1).returning(|_|Some("test_value".to_string()));letservice=DataService::new(Box::new(mock_repo));letresult=service.get_data("test_key");assert_eq!(result,Some("test_value".to_string()));}#[test]fntest_get_data_not_found(){letmutmock_repo=MockDataRepository::new();mock_repo.expect_get().with(eq("nonexistent_key")).times(1).returning(|_|None);letservice=DataService::new(Box::new(mock_repo));letresult=service.get_data("nonexistent_key");assert_eq!(result,None);}#[test]fntest_set_data_success(){letmutmock_repo=MockDataRepository::new();mock_repo.expect_set().with(eq("test_key".to_string()),eq("test_value".to_string())).times(1).returning(|_,_|Ok(()));letmutservice=DataService::new(Box::new(mock_repo));letresult=service.set_data("test_key".to_string(),"test_value".to_string());assert!(result.is_ok());}#[test]fntest_delete_data_success(){letmutmock_repo=MockDataRepository::new();mock_repo.expect_delete().with(eq("test_key")).times(1).returning(|_|Ok(()));letmutservice=DataService::new(Box::new(mock_repo));letresult=service.delete_data("test_key");assert!(result.is_ok());}#[test]fntest_data_exists_true(){letmutmock_repo=MockDataRepository::new();mock_repo.expect_exists().with(eq("test_key")).times(1).returning(|_|true);letservice=DataService::new(Box::new(mock_repo));letresult=service.data_exists("test_key");assert!(result);}#[test]fntest_get_or_set_default_existing(){letmutmock_repo=MockDataRepository::new();mock_repo.expect_get().with(eq("existing_key")).times(1).returning(|_|Some("existing_value".to_string()));letmutservice=DataService::new(Box::new(mock_repo));letresult=service.get_or_set_default("existing_key".to_string(),"default_value".to_string()).unwrap();assert_eq!(result,"existing_value");}#[test]fntest_get_or_set_default_missing(){letmutmock_repo=MockDataRepository::new();mock_repo.expect_get().with(eq("missing_key")).times(1).returning(|_|None);mock_repo.expect_set().with(eq("missing_key".to_string()),eq("default_value".to_string())).times(1).returning(|_,_|Ok(()));letmutservice=DataService::new(Box::new(mock_repo));letresult=service.get_or_set_default("missing_key".to_string(),"default_value".to_string()).unwrap();assert_eq!(result,"default_value");}}
// rust/04-integration-testing.rs/*Integration testing patterns for Rust applications*/usestd::collections::HashMap;useanyhow::Result;useserde::{Deserialize,Serialize};#[derive(Debug, Clone, Serialize, Deserialize)]pubstructUser{pubid:u64,pubname:String,pubemail:String,pubage:u32,}#[derive(Debug, Clone, Serialize, Deserialize)]pubstructUserUpdate{pubname:Option<String>,pubemail:Option<String>,pubage:Option<u32>,}pubstructUserService{users:HashMap<u64,User>,next_id:u64,}implUserService{pubfnnew()->Self{Self{users:HashMap::new(),next_id:1,}}pubfncreate_user(&mutself,name:String,email:String,age:u32)->Result<User>{ifname.is_empty(){returnErr(anyhow::anyhow!("Name cannot be empty"));}ifemail.is_empty(){returnErr(anyhow::anyhow!("Email cannot be empty"));}ifage>150{returnErr(anyhow::anyhow!("Age must be less than 150"));}letuser=User{id:self.next_id,name,email,age,};self.users.insert(user.id,user.clone());self.next_id+=1;Ok(user)}pubfnget_user(&self,id:u64)->Option<&User>{self.users.get(&id)}pubfnupdate_user(&mutself,id:u64,update:UserUpdate)->Result<User>{letuser=self.users.get_mut(&id).ok_or_else(||anyhow::anyhow!("User not found"))?;ifletSome(name)=update.name{ifname.is_empty(){returnErr(anyhow::anyhow!("Name cannot be empty"));}user.name=name;}ifletSome(email)=update.email{ifemail.is_empty(){returnErr(anyhow::anyhow!("Email cannot be empty"));}user.email=email;}ifletSome(age)=update.age{ifage>150{returnErr(anyhow::anyhow!("Age must be less than 150"));}user.age=age;}Ok(user.clone())}pubfndelete_user(&mutself,id:u64)->Result<User>{self.users.remove(&id).ok_or_else(||anyhow::anyhow!("User not found"))}pubfnlist_users(&self)->Vec<&User>{self.users.values().collect()}pubfnsearch_users(&self,query:&str)->Vec<&User>{self.users.values().filter(|user|user.name.contains(query)||user.email.contains(query)).collect()}}// Integration tests#[cfg(test)]modintegration_tests{usesuper::*;#[test]fntest_full_user_lifecycle(){letmutservice=UserService::new();// Create userletuser=service.create_user("John Doe".to_string(),"john@example.com".to_string(),30).unwrap();assert_eq!(user.id,1);assert_eq!(user.name,"John Doe");assert_eq!(user.email,"john@example.com");assert_eq!(user.age,30);// Get userletretrieved_user=service.get_user(user.id).unwrap();assert_eq!(retrieved_user.name,"John Doe");// Update userletupdate=UserUpdate{name:Some("John Smith".to_string()),email:None,age:Some(31),};letupdated_user=service.update_user(user.id,update).unwrap();assert_eq!(updated_user.name,"John Smith");assert_eq!(updated_user.age,31);assert_eq!(updated_user.email,"john@example.com");// Unchanged// Search usersletsearch_results=service.search_users("John");assert_eq!(search_results.len(),1);assert_eq!(search_results[0].name,"John Smith");// Delete userletdeleted_user=service.delete_user(user.id).unwrap();assert_eq!(deleted_user.name,"John Smith");// Verify deletionassert!(service.get_user(user.id).is_none());}#[test]fntest_multiple_users(){letmutservice=UserService::new();// Create multiple usersletuser1=service.create_user("Alice".to_string(),"alice@example.com".to_string(),25).unwrap();letuser2=service.create_user("Bob".to_string(),"bob@example.com".to_string(),30).unwrap();letuser3=service.create_user("Charlie".to_string(),"charlie@example.com".to_string(),35).unwrap();// List all usersletall_users=service.list_users();assert_eq!(all_users.len(),3);// Search usersletsearch_results=service.search_users("example.com");assert_eq!(search_results.len(),3);// Update one userletupdate=UserUpdate{name:Some("Alice Smith".to_string()),email:None,age:None,};service.update_user(user1.id,update).unwrap();// Verify updateletupdated_user=service.get_user(user1.id).unwrap();assert_eq!(updated_user.name,"Alice Smith");// Delete one userservice.delete_user(user2.id).unwrap();// Verify deletionletremaining_users=service.list_users();assert_eq!(remaining_users.len(),2);assert!(service.get_user(user2.id).is_none());}#[test]fntest_error_handling(){letmutservice=UserService::new();// Test empty nameletresult=service.create_user("".to_string(),"test@example.com".to_string(),25);assert!(result.is_err());assert!(result.unwrap_err().to_string().contains("Name cannot be empty"));// Test empty emailletresult=service.create_user("Test".to_string(),"".to_string(),25);assert!(result.is_err());assert!(result.unwrap_err().to_string().contains("Email cannot be empty"));// Test invalid ageletresult=service.create_user("Test".to_string(),"test@example.com".to_string(),200);assert!(result.is_err());assert!(result.unwrap_err().to_string().contains("Age must be less than 150"));// Test updating non-existent userletupdate=UserUpdate{name:Some("New Name".to_string()),email:None,age:None,};letresult=service.update_user(999,update);assert!(result.is_err());assert!(result.unwrap_err().to_string().contains("User not found"));// Test deleting non-existent userletresult=service.delete_user(999);assert!(result.is_err());assert!(result.unwrap_err().to_string().contains("User not found"));}}