mock-strategy-guide
Guides users on creating mock implementations for testing with traits, providing test doubles, and avoiding tight coupling to test infrastructure. Activates when users need to test code with external dependencies.
What this skill does
# Mock Strategy Guide Skill
You are an expert at testing strategies for Rust, especially creating mock implementations for hexagonal architecture. When you detect testing needs for code with dependencies, proactively suggest mocking strategies.
## When to Activate
Activate when you notice:
- Code with external dependencies (DB, HTTP, etc.)
- Trait-based abstractions for repositories or services
- Tests that require real infrastructure
- Questions about mocking or test doubles
## Mock Implementation Patterns
### Pattern 1: Simple Mock Repository
```rust
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
struct MockUserRepository {
users: HashMap<String, User>,
}
impl MockUserRepository {
fn new() -> Self {
Self {
users: HashMap::new(),
}
}
fn with_user(mut self, user: User) -> Self {
self.users.insert(user.id.clone(), user);
self
}
}
#[async_trait]
impl UserRepository for MockUserRepository {
async fn find(&self, id: &str) -> Result<User, Error> {
self.users
.get(id)
.cloned()
.ok_or(Error::NotFound)
}
async fn save(&self, user: &User) -> Result<(), Error> {
// Mock just succeeds
Ok(())
}
}
#[tokio::test]
async fn test_user_service() {
// Arrange
let user = User { id: "1".to_string(), email: "[email protected]".to_string() };
let mock_repo = MockUserRepository::new().with_user(user.clone());
let service = UserService::new(mock_repo);
// Act
let result = service.get_user("1").await;
// Assert
assert!(result.is_ok());
assert_eq!(result.unwrap().id, "1");
}
}
```
### Pattern 2: Mock with Verification
```rust
#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
struct MockEmailService {
sent_emails: Arc<Mutex<Vec<Email>>>,
}
impl MockEmailService {
fn new() -> Self {
Self {
sent_emails: Arc::new(Mutex::new(Vec::new())),
}
}
fn emails_sent(&self) -> Vec<Email> {
self.sent_emails.lock().unwrap().clone()
}
}
#[async_trait]
impl EmailService for MockEmailService {
async fn send(&self, email: Email) -> Result<(), Error> {
self.sent_emails.lock().unwrap().push(email);
Ok(())
}
}
#[tokio::test]
async fn test_sends_welcome_email() {
let mock_email = MockEmailService::new();
let service = UserService::new(mock_email.clone());
service.register_user("[email protected]").await.unwrap();
// Verify email was sent
let emails = mock_email.emails_sent();
assert_eq!(emails.len(), 1);
assert_eq!(emails[0].to, "[email protected]");
assert!(emails[0].subject.contains("Welcome"));
}
}
```
### Pattern 3: Mock with Controlled Failures
```rust
#[cfg(test)]
mod tests {
enum MockBehavior {
Success,
NotFound,
DatabaseError,
}
struct MockRepository {
behavior: MockBehavior,
}
impl MockRepository {
fn with_behavior(behavior: MockBehavior) -> Self {
Self { behavior }
}
}
#[async_trait]
impl UserRepository for MockRepository {
async fn find(&self, id: &str) -> Result<User, Error> {
match self.behavior {
MockBehavior::Success => Ok(test_user()),
MockBehavior::NotFound => Err(Error::NotFound),
MockBehavior::DatabaseError => Err(Error::Database("Connection failed".into())),
}
}
}
#[tokio::test]
async fn test_handles_not_found() {
let mock = MockRepository::with_behavior(MockBehavior::NotFound);
let service = UserService::new(mock);
let result = service.get_user("1").await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::NotFound));
}
#[tokio::test]
async fn test_handles_database_error() {
let mock = MockRepository::with_behavior(MockBehavior::DatabaseError);
let service = UserService::new(mock);
let result = service.get_user("1").await;
assert!(result.is_err());
}
}
```
### Pattern 4: Builder Pattern for Mocks
```rust
#[cfg(test)]
mod tests {
struct MockRepositoryBuilder {
users: HashMap<String, User>,
find_error: Option<Error>,
save_error: Option<Error>,
}
impl MockRepositoryBuilder {
fn new() -> Self {
Self {
users: HashMap::new(),
find_error: None,
save_error: None,
}
}
fn with_user(mut self, user: User) -> Self {
self.users.insert(user.id.clone(), user);
self
}
fn with_find_error(mut self, error: Error) -> Self {
self.find_error = Some(error);
self
}
fn build(self) -> MockRepository {
MockRepository {
users: self.users,
find_error: self.find_error,
save_error: self.save_error,
}
}
}
#[tokio::test]
async fn test_with_builder() {
let mock = MockRepositoryBuilder::new()
.with_user(test_user())
.with_save_error(Error::Database("Save failed".into()))
.build();
let service = UserService::new(mock);
// Can find user
let user = service.get_user("1").await.unwrap();
// But save fails
let result = service.update_user(user).await;
assert!(result.is_err());
}
}
```
## In-Memory Test Implementations
For integration tests with real logic but no infrastructure:
```rust
pub struct InMemoryUserRepository {
users: Arc<Mutex<HashMap<String, User>>>,
}
impl InMemoryUserRepository {
pub fn new() -> Self {
Self {
users: Arc::new(Mutex::new(HashMap::new())),
}
}
}
#[async_trait]
impl UserRepository for InMemoryUserRepository {
async fn find(&self, id: &str) -> Result<User, Error> {
self.users
.lock()
.unwrap()
.get(id)
.cloned()
.ok_or(Error::NotFound)
}
async fn save(&self, user: &User) -> Result<(), Error> {
self.users
.lock()
.unwrap()
.insert(user.id.clone(), user.clone());
Ok(())
}
async fn delete(&self, id: &str) -> Result<(), Error> {
self.users
.lock()
.unwrap()
.remove(id)
.ok_or(Error::NotFound)?;
Ok(())
}
}
```
## Test Fixture Helpers
```rust
#[cfg(test)]
mod fixtures {
use super::*;
pub fn test_user() -> User {
User {
id: "test-id".to_string(),
email: "[email protected]".to_string(),
name: "Test User".to_string(),
}
}
pub fn test_user_with_id(id: &str) -> User {
User {
id: id.to_string(),
email: "[email protected]".to_string(),
name: "Test User".to_string(),
}
}
pub fn test_users(count: usize) -> Vec<User> {
(0..count)
.map(|i| test_user_with_id(&format!("user-{}", i)))
.collect()
}
}
```
## Your Approach
When you see code needing tests:
1. Identify external dependencies (traits)
2. Suggest mock implementation structure
3. Show verification patterns
4. Provide test fixture helpers
When you see tests without mocks:
1. Suggest extracting trait if tightly coupled
2. Show how to create mock implementations
3. Demonstrate verification patterns
Proactively suggest mocking strategies for testable, maintainable code.
Related in Code Review
gstack
IncludedFast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack)
startup-due-diligence
IncludedLegal due diligence review for seed-stage and Series A startups (US, Delaware C-Corp focus). Supports both investor and founder perspectives. Capabilities include: (1) Interactive document review and issue spotting; (2) Document request list generation; (3) Cap table and SAFE/convertible note analysis; (4) Red flag identification with severity ratings; (5) Diligence report generation. TRIGGERS: due diligence, DD, startup investment, cap table review, Series A, seed round, investor diligence, legal review startup, SAFE analysis, convertible note, 409A, founder vesting.
interview-master
IncludedThis skill should be used when the user asks to "generate interview questions", "prepare for interview", "optimize resume", "conduct mock interview", "analyze git commits for resume", "generate resume from code", "review my resume", or mentions interview preparation, career assistance, or extracting project experience from git history. Provides comprehensive interview and career development guidance for both job seekers and interviewers.
fix-issue
IncludedFixes GitHub issues using parallel analysis agents for root cause investigation, code exploration, and regression detection. Reads issue context from gh CLI, searches codebase and memory for related patterns, generates a fix with tests, and links the resolution back to the issue via PR. Includes prevention analysis to avoid recurrence. Use when debugging errors, resolving regressions, fixing bugs, or triaging issues.
sf-apex
IncludedGenerates and reviews Salesforce Apex code with 150-point scoring. TRIGGER when: user writes, reviews, or fixes Apex classes, triggers, test classes, batch/queueable/schedulable jobs, or touches .cls/.trigger files. DO NOT TRIGGER when: LWC JavaScript (use sf-lwc), Flow XML (use sf-flow), SOQL-only queries (use sf-soql), or non-Salesforce code.
swift-development
IncludedComprehensive Swift development for building, testing, and deploying iOS/macOS applications. Use when Claude needs to: (1) Build Swift packages or Xcode projects from command line, (2) Run tests with XCTest or Swift Testing framework, (3) Manage iOS simulators with simctl, (4) Handle code signing, provisioning profiles, and app distribution, (5) Format or lint Swift code with SwiftFormat/SwiftLint, (6) Work with Swift Package Manager (SPM), (7) Implement Swift 6 concurrency patterns (async/await, actors, Sendable), (8) Create SwiftUI views with MVVM architecture, (9) Set up Core Data or SwiftData persistence, or any other Swift/iOS/macOS development tasks.