Use when writing or changing tests, adding mocks - prevents testing mock behavior, production pollution with test-only methods, and mocking without understanding dependencies
Prevents testing mock behavior, adding test-only methods to production, and mocking without understanding. Use when writing tests or adding mocks—applies strict gate functions before assertions, production changes, or mocking decisions.
/plugin marketplace add withzombies/hyperpowers/plugin install withzombies-hyper@withzombies-hyperThis skill inherits all available tools. When active, it can use any tool Claude has access to.
<skill_overview> Tests must verify real behavior, not mock behavior; mocks are tools to isolate, not things to test. </skill_overview>
<rigidity_level> LOW FREEDOM - The 3 Iron Laws are absolute (never test mocks, never add test-only methods, never mock without understanding). Apply gate functions strictly. </rigidity_level>
<quick_reference>
Before asserting on any mock:
Before adding method to production:
Before mocking:
<when_to_use>
Critical moment: Before you add a mock or test-only method, use this skill's gate functions. </when_to_use>
<the_iron_laws>
Anti-pattern:
// ❌ BAD: Testing that mock exists
#[test]
fn test_processes_request() {
let mock_service = MockApiService::new();
let handler = RequestHandler::new(Box::new(mock_service));
// Testing mock existence, not behavior
assert!(handler.service().is_mock());
}
Why wrong: Verifies mock works, not that code works.
Fix:
// ✅ GOOD: Test real behavior
#[test]
fn test_processes_request() {
let service = TestApiService::new(); // Real implementation or full fake
let handler = RequestHandler::new(Box::new(service));
let result = handler.process_request("data");
assert_eq!(result.status, StatusCode::OK);
}
Anti-pattern:
// ❌ BAD: reset() only used in tests
pub struct Connection {
pool: Arc<ConnectionPool>,
}
impl Connection {
pub fn reset(&mut self) { // Looks like production API!
self.pool.clear_all();
}
}
// In tests
#[test]
fn test_something() {
let mut conn = Connection::new();
conn.reset(); // Test-only method
}
Why wrong:
Fix:
// ✅ GOOD: Test utilities handle cleanup
// Connection has no reset()
// In tests/test_utils.rs
pub fn cleanup_connection(conn: &Connection) {
if let Some(pool) = conn.get_pool() {
pool.clear_test_data();
}
}
// In tests
#[test]
fn test_something() {
let conn = Connection::new();
cleanup_connection(&conn);
}
Anti-pattern:
// ❌ BAD: Mock breaks test logic
#[test]
fn test_detects_duplicate_server() {
// Mock prevents config write that test depends on!
let mut config_manager = MockConfigManager::new();
config_manager.expect_add_server()
.returning(|_| Ok(())); // No actual config write!
config_manager.add_server(&config).unwrap();
config_manager.add_server(&config).unwrap(); // Should fail - but won't!
}
Why wrong: Mocked method had side effect test depended on (writing config).
Fix:
// ✅ GOOD: Mock at correct level
#[test]
fn test_detects_duplicate_server() {
// Mock the slow part, preserve behavior test needs
let server_manager = MockServerManager::new(); // Just mock slow server startup
let config_manager = ConfigManager::new_with_manager(server_manager);
config_manager.add_server(&config).unwrap(); // Config written
let result = config_manager.add_server(&config); // Duplicate detected ✓
assert!(result.is_err());
}
</the_iron_laws>
<gate_functions>
BEFORE any assertion that checks mock elements:
1. Ask: "Am I testing real component behavior or just mock existence?"
2. If testing mock existence:
STOP - Delete the assertion or unmock the component
3. Test real behavior instead
Examples of mock existence testing (all wrong):
assert!(handler.service().is_mock())XCTAssertTrue(manager.delegate is MockDelegate)expect(component.database).toBe(mockDb)BEFORE adding any method to production class:
1. Ask: "Is this only used by tests?"
2. If yes:
STOP - Don't add it
Put it in test utilities instead
3. Ask: "Does this class own this resource's lifecycle?"
4. If no:
STOP - Wrong class for this method
Red flags:
reset(), clear(), cleanup() in production class#[cfg(test)] callersBEFORE mocking any method:
STOP - Don't mock yet
1. Ask: "What side effects does the real method have?"
2. Ask: "Does this test depend on any of those side effects?"
3. Ask: "Do I fully understand what this test needs?"
If depends on side effects:
→ Mock at lower level (the actual slow/external operation)
→ OR use test doubles that preserve necessary behavior
→ NOT the high-level method the test depends on
If unsure what test depends on:
→ Run test with real implementation FIRST
→ Observe what actually needs to happen
→ THEN add minimal mocking at the right level
Red flags:
// Testing that mock exists
assert_eq!(service.database().connection_string(), "mock://test");
assert!(service.database().is_test_mode());
} </code>
<why_it_fails>
"Am I testing real behavior or mock existence?" → Testing mock existence (connection_string(), is_test_mode() are mock properties)
Fix:
#[test]
fn test_user_service_creates_user() {
let db = TestDatabase::new(); // Real test implementation
let service = UserService::new(db);
// Test real behavior
let user = service.create_user("alice", "alice@example.com").unwrap();
assert_eq!(user.name, "alice");
assert_eq!(user.email, "alice@example.com");
// Verify user was saved
let retrieved = service.get_user(user.id).unwrap();
assert_eq!(retrieved.name, "alice");
}
What you gain:
impl Database { pub fn new() -> Self { /* ... */ }
// Added "for testing"
pub fn reset(&mut self) {
self.pool.clear();
self.pool.reinitialize();
}
}
// Tests #[test] fn test_user_creation() { let mut db = Database::new(); // ... test logic ... db.reset(); // Clean up }
#[test] fn test_user_deletion() { let mut db = Database::new(); // ... test logic ... db.reset(); // Clean up } </code>
<why_it_fails>
"Is this only used by tests?" → YES "Does Database class own test lifecycle?" → NO
Fix:
// Production code (NO reset method)
pub struct Database {
pool: ConnectionPool,
}
impl Database {
pub fn new() -> Self { /* ... */ }
// No reset() - production code clean
}
// Test utilities (tests/test_utils.rs)
pub fn create_test_database() -> Database {
Database::new()
}
pub fn cleanup_database(db: &mut Database) {
// Access internals properly for cleanup
if let Some(pool) = db.get_pool_mut() {
pool.clear_test_data();
}
}
// Tests
#[test]
fn test_user_creation() {
let mut db = create_test_database();
// ... test logic ...
cleanup_database(&mut db);
}
What you gain:
// Test expects duplicate detection
mock_config.add_server(&server_config).unwrap();
let result = mock_config.add_server(&server_config);
// Assertion fails! Mock always returns Ok, no duplicate detection
assert!(result.is_err()); // FAILS
} </code>
<why_it_fails>
"What side effects does add_server() have?" → Writes to config file, tracks added servers "Does test depend on those?" → YES! Test needs duplicate detection "Do I understand what test needs?" → Now yes
Fix:
#[test]
fn test_detects_duplicate_server() {
// Mock at the RIGHT level - just the slow I/O
let mock_file_system = MockFileSystem::new(); // Mock slow file writes
let config_manager = ConfigManager::new_with_fs(mock_file_system);
// ConfigManager's duplicate detection still works
config_manager.add_server(&server_config).unwrap();
let result = config_manager.add_server(&server_config);
// Passes! ConfigManager tracks duplicates, only file I/O is mocked
assert!(result.is_err());
}
What you gain:
<additional_anti_patterns>
Problem: Mock only fields you think you need, omit others.
// ❌ BAD: Partial mock
struct MockResponse {
status: String,
data: UserData,
// Missing: metadata that downstream code uses
}
impl ApiResponse for MockResponse {
fn metadata(&self) -> &Metadata {
panic!("metadata not implemented!") // Breaks at runtime!
}
}
Fix: Mirror real API completely.
// ✅ GOOD: Complete mock
struct MockResponse {
status: String,
data: UserData,
metadata: Metadata, // All fields real API returns
}
Gate function:
BEFORE creating mock responses:
1. Examine actual API response structure
2. Include ALL fields system might consume
3. Verify mock matches real schema completely
Warning signs:
Consider: Integration tests with real components often simpler than complex mocks. </additional_anti_patterns>
<tdd_prevention>
Why TDD helps:
If you're testing mock behavior, you violated TDD - you added mocks without watching test fail against real code first.
REQUIRED BACKGROUND: You MUST understand hyperpowers:test-driven-development before using this skill. </tdd_prevention>
<critical_rules>
All of these mean: STOP. Apply the gate function.
<verification_checklist> Before claiming tests are correct:
is_mock(), is MockType, etc.)Can't check all boxes? Apply gate functions and refactor. </verification_checklist>
<integration> **This skill requires:** - hyperpowers:test-driven-development (prevents these anti-patterns) - Understanding of mocking vs. faking vs. stubbingThis skill is called by:
Red flags triggering this skill:
*-mock test IDsWhen stuck:
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.