From claude-initial-setup
Mocks vs stubs vs spies, dependency injection for testing, jest.mock/unittest.mock/gomock, and when NOT to mock. Use when the user needs to isolate dependencies in tests, asks about mocking external services, or has tests tightly coupled to implementation details.
npx claudepluginhub versoxbt/claude-initial-setup --plugin claude-initial-setupThis skill uses the workspace's default tool permissions.
Mocking isolates the code under test from its dependencies. Done right, mocks make tests fast and focused. Done wrong, they create brittle tests coupled to implementation details.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Mocking isolates the code under test from its dependencies. Done right, mocks make tests fast and focused. Done wrong, they create brittle tests coupled to implementation details.
| Type | Purpose | Verifies Calls? | Returns Data? |
|---|---|---|---|
| Stub | Replace with canned response | No | Yes |
| Spy | Observe calls without replacing | Yes | Original |
| Mock | Replace + verify calls + canned response | Yes | Yes |
// STUB: Replace function, return fixed data
const getUser = vi.fn().mockResolvedValue({ id: 1, name: 'Alice' })
// SPY: Watch real function, keep original behavior
const spy = vi.spyOn(userService, 'getUser')
// Real getUser still runs, but calls are recorded
// MOCK: Replace function, return fixed data, verify calls
const sendEmail = vi.fn().mockResolvedValue({ sent: true })
await notifyUser(1)
expect(sendEmail).toHaveBeenCalledWith('alice@example.com', expect.any(String))
Module mock:
import { describe, it, expect, vi } from 'vitest'
import { processOrder } from './orders'
// Mock the entire payment module
vi.mock('./payment', () => ({
chargeCard: vi.fn().mockResolvedValue({ success: true, txId: 'tx-123' }),
}))
import { chargeCard } from './payment'
describe('processOrder', () => {
it('charges the card and returns order confirmation', async () => {
const order = await processOrder({ userId: 1, amount: 99 })
expect(chargeCard).toHaveBeenCalledWith({ userId: 1, amount: 99 })
expect(order.status).toBe('confirmed')
expect(order.transactionId).toBe('tx-123')
})
it('handles payment failure', async () => {
vi.mocked(chargeCard).mockRejectedValueOnce(new Error('Card declined'))
await expect(processOrder({ userId: 1, amount: 99 }))
.rejects.toThrow('Card declined')
})
})
Spy on object method:
const spy = vi.spyOn(console, 'error').mockImplementation(() => {})
await riskyOperation()
expect(spy).toHaveBeenCalledWith('Operation failed:', expect.any(Error))
spy.mockRestore()
Patching with context manager:
from unittest.mock import patch, MagicMock
def test_send_notification():
with patch('myapp.services.email.send') as mock_send:
mock_send.return_value = {"sent": True}
result = notify_user(user_id=1, message="Hello")
mock_send.assert_called_once_with(
to="alice@example.com",
body="Hello"
)
assert result["sent"] is True
Patching with decorator:
@patch('myapp.services.payment.charge_card')
def test_process_order(mock_charge):
mock_charge.return_value = {"success": True, "tx_id": "tx-123"}
order = process_order(user_id=1, amount=99)
mock_charge.assert_called_once_with(user_id=1, amount=99)
assert order["status"] == "confirmed"
Mock side effects for error testing:
@patch('myapp.services.payment.charge_card')
def test_payment_failure(mock_charge):
mock_charge.side_effect = PaymentError("Card declined")
with pytest.raises(PaymentError, match="Card declined"):
process_order(user_id=1, amount=99)
Design code to accept dependencies, making them mockable:
// HARD TO TEST: Direct import
import { db } from './database'
export async function getUser(id: string) {
return db.query('SELECT * FROM users WHERE id = $1', [id])
}
// EASY TO TEST: Dependency injection
export function createUserService(db: Database) {
return {
async getUser(id: string) {
return db.query('SELECT * FROM users WHERE id = $1', [id])
},
}
}
// In tests:
const mockDb = { query: vi.fn().mockResolvedValue({ id: '1', name: 'Alice' }) }
const service = createUserService(mockDb)
const user = await service.getUser('1')
Do not mock these — use the real thing:
1. Pure functions (no side effects, deterministic)
createSlug('Hello World') -> test with real input/output
2. Value objects and data transformations
formatDate(date) -> test with real dates
3. Your own code under test
Do not mock the function you are testing
4. Standard library / language built-ins
Do not mock Array.map, string methods, Math.random (seed instead)
5. Simple internal utilities
If formatCurrency() is fast and pure, call it for real
Mock only at system boundaries:
For time-dependent code, use fake timers instead of mocking Date:
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'
describe('session expiry', () => {
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.useRealTimers()
})
it('expires session after 30 minutes', () => {
const session = createSession()
expect(session.isValid()).toBe(true)
vi.advanceTimersByTime(30 * 60 * 1000) // 30 minutes
expect(session.isValid()).toBe(false)
})
})
expect(mock).toHaveBeenCalled() without asserting the result tests that you called a mock, not that your code works. Always assert on output or observable behavior.mockRestore() or afterEach(() => vi.restoreAllMocks()) leaks mock state between tests, causing flaky failures.| Scenario | Strategy |
|---|---|
| External API call | Mock the HTTP client |
| Database query | Mock the repository/query function |
| Time-dependent logic | Fake timers (vi.useFakeTimers()) |
| Error paths | mockRejectedValue / side_effect |
| Pure functions | Do NOT mock — test with real input |
| Internal utilities | Do NOT mock — call for real |
| File I/O | Mock the fs calls or use temp directory |
Mock at boundaries: External services, databases, filesystem, network.
Do not mock: Pure functions, value objects, the code under test itself.
Always restore: vi.restoreAllMocks() in afterEach or use mockRestore().
DI for testability: Accept dependencies as parameters, not hard imports.