Fake Examples — Supplementary Patterns
Examples that complement the core patterns in the SKILL.md. These cover additional scenarios: configurable failure, notification fakes, inventory domain, and testutils crate layout.
Fake with Configurable Failure Modes
#![allow(unused)] fn main() { use std::sync::Mutex; pub struct FakeNotifier { notifications: Mutex<Vec<(UserId, String)>>, fail_on: Mutex<Option<String>>, } impl FakeNotifier { pub fn new() -> Self { Self { notifications: Mutex::new(Vec::new()), fail_on: Mutex::new(None), } } pub fn fail_on(&self, notification_type: &str) { *self.fail_on.lock().unwrap() = Some(notification_type.into()); } pub fn was_notified(&self, user_id: &UserId, notification_type: &str) -> bool { self.notifications.lock().unwrap().iter() .any(|(id, t)| id == user_id && t == notification_type) } pub fn all_notifications(&self) -> Vec<(UserId, String)> { self.notifications.lock().unwrap().clone() } } impl Notifier for FakeNotifier { fn send_welcome(&self, user: &User) -> Result<(), NotifyError> { if self.fail_on.lock().unwrap().as_deref() == Some("welcome") { return Err(NotifyError::SendFailed("fake failure".into())); } self.notifications.lock().unwrap().push((user.id.clone(), "welcome".into())); Ok(()) } fn send_password_reset(&self, user: &User, _token: &str) -> Result<(), NotifyError> { if self.fail_on.lock().unwrap().as_deref() == Some("password_reset") { return Err(NotifyError::SendFailed("fake failure".into())); } self.notifications.lock().unwrap().push((user.id.clone(), "password_reset".into())); Ok(()) } } }
Second Domain Example — Inventory
A different domain to show the pattern generalizes. Same structure: trait in core, fake in testutils.
Trait (core crate)
#![allow(unused)] fn main() { #[derive(Debug, Clone, PartialEq)] pub struct ItemId(pub String); #[derive(Debug, Clone)] pub struct Item { pub id: ItemId, pub name: String, pub quantity: u32, } #[derive(Debug, Error)] pub enum InventoryError { #[error("item not found: {0:?}")] NotFound(ItemId), #[error("insufficient stock: have {available}, need {requested}")] InsufficientStock { available: u32, requested: u32 }, #[error("storage error: {0}")] Storage(String), } pub trait InventoryRepository: Send + Sync { fn get(&self, id: &ItemId) -> Result<Option<Item>, InventoryError>; fn save(&self, item: &Item) -> Result<(), InventoryError>; fn reserve(&self, id: &ItemId, quantity: u32) -> Result<Item, InventoryError>; } }
Fake (testutils crate)
#![allow(unused)] fn main() { use std::sync::Mutex; pub struct FakeInventoryRepository { items: Mutex<Vec<Item>>, should_fail: Mutex<bool>, } impl FakeInventoryRepository { pub fn new() -> Self { Self { items: Mutex::new(Vec::new()), should_fail: Mutex::new(false) } } pub fn with_items(items: Vec<Item>) -> Self { Self { items: Mutex::new(items), should_fail: Mutex::new(false) } } pub fn set_should_fail(&self, fail: bool) { *self.should_fail.lock().unwrap() = fail; } pub fn get_item(&self, id: &ItemId) -> Option<Item> { self.items.lock().unwrap().iter().find(|i| i.id == *id).cloned() } } impl InventoryRepository for FakeInventoryRepository { fn get(&self, id: &ItemId) -> Result<Option<Item>, InventoryError> { if *self.should_fail.lock().unwrap() { return Err(InventoryError::Storage("fake failure".into())); } Ok(self.items.lock().unwrap().iter().find(|i| i.id == *id).cloned()) } fn save(&self, item: &Item) -> Result<(), InventoryError> { if *self.should_fail.lock().unwrap() { return Err(InventoryError::Storage("fake failure".into())); } let mut items = self.items.lock().unwrap(); if let Some(pos) = items.iter().position(|i| i.id == item.id) { items[pos] = item.clone(); } else { items.push(item.clone()); } Ok(()) } fn reserve(&self, id: &ItemId, quantity: u32) -> Result<Item, InventoryError> { if *self.should_fail.lock().unwrap() { return Err(InventoryError::Storage("fake failure".into())); } let mut items = self.items.lock().unwrap(); let item = items.iter_mut() .find(|i| i.id == *id) .ok_or_else(|| InventoryError::NotFound(id.clone()))?; if item.quantity < quantity { return Err(InventoryError::InsufficientStock { available: item.quantity, requested: quantity, }); } item.quantity -= quantity; Ok(item.clone()) } } }
Item Builder
#![allow(unused)] fn main() { pub struct ItemBuilder { id: String, name: String, quantity: u32, } impl Default for ItemBuilder { fn default() -> Self { Self { id: "item-1".into(), name: "Test Item".into(), quantity: 100 } } } impl ItemBuilder { pub fn with_id(mut self, id: impl Into<String>) -> Self { self.id = id.into(); self } pub fn with_name(mut self, name: impl Into<String>) -> Self { self.name = name.into(); self } pub fn with_quantity(mut self, quantity: u32) -> Self { self.quantity = quantity; self } pub fn out_of_stock(mut self) -> Self { self.quantity = 0; self } pub fn build(self) -> Item { Item { id: ItemId(self.id), name: self.name, quantity: self.quantity } } } pub fn an_item() -> ItemBuilder { ItemBuilder::default() } }
Testutils Crate Layout
crates/my-testutils/
├── Cargo.toml
└── src/
├── lib.rs
├── fakes/
│ ├── mod.rs # pub use each fake
│ ├── user.rs # FakeUserRepository
│ ├── inventory.rs # FakeInventoryRepository
│ └── notifier.rs # FakeNotifier
└── builders/
├── mod.rs # pub use each builder
├── user.rs # UserBuilder, a_user()
└── item.rs # ItemBuilder, an_item()
# crates/my-testutils/Cargo.toml
[package]
name = "my-testutils"
version = "0.1.0"
edition.workspace = true
publish = false
[lints]
workspace = true
[dependencies]
my-core = { path = "../my-core" }
Example Integration Test
#![allow(unused)] fn main() { use my_testutils::{FakeInventoryRepository, FakeNotifier, an_item, a_user}; use std::sync::Arc; #[test] fn placing_order_reserves_stock_and_notifies_user() { let inventory = Arc::new(FakeInventoryRepository::with_items(vec![ an_item().with_id("widget").with_quantity(10).build() ])); let users = Arc::new(FakeUserRepository::with_users(vec![ a_user().with_id("alice").build() ])); let notifier = Arc::new(FakeNotifier::new()); let service = OrderService::new( Arc::clone(&inventory) as Arc<dyn InventoryRepository>, Arc::clone(&users) as Arc<dyn UserRepository>, Arc::clone(¬ifier) as Arc<dyn Notifier>, ); let order = service.place_order("alice", "widget", 3).unwrap(); assert_eq!(order.quantity, 3); assert_eq!(inventory.get_item(&ItemId("widget".into())).unwrap().quantity, 7); assert!(notifier.was_notified(&UserId("alice".into()), "order_confirmation")); } }