agentsclimarketplace

Mocking strategies

Skill VersoXBT/claude-initial-setup/skills/testing/mocking-strategies

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.From its SKILL.md

Install
npx -y skills add VersoXBT/claude-initial-setup --skill mocking-strategies

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

7.5 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it

Mocking Strategies

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.

When to Use

  • User needs to test code that calls external APIs or databases
  • User asks about mocks, stubs, spies, or fakes
  • Tests are slow because they hit real services
  • User wants to test error paths that are hard to trigger naturally
  • Tests are brittle and break on internal refactors

Core Patterns

Mocks vs Stubs vs Spies

TypePurposeVerifies Calls?Returns Data?
StubReplace with canned responseNoYes
SpyObserve calls without replacingYesOriginal
MockReplace + verify calls + canned responseYesYes
// 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('[email protected]', expect.any(String))

Vitest / Jest Mocking

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()

Python unittest.mock

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="[email protected]",
            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)

Dependency Injection for Testability

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')

When NOT to Mock

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:

  • External HTTP APIs
  • Database queries
  • File system operations
  • Email/SMS services
  • Payment processors
  • Clock/time (use fake timers instead)

Fake Timers

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)
  })
})

Anti-Patterns

  • Mocking everything: If you mock all dependencies, you are testing the mocking framework, not your code. Mock at boundaries; let internal code run for real.
  • Testing mock behavior: 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.
  • Mocking what you do not own without integration tests: Mocking a third-party API is fine for unit tests, but you also need integration tests that call the real API to catch contract changes.
  • Implementation-coupled mocks: If a refactor (same behavior, different internal calls) breaks your tests, the mocks are too specific. Mock at a higher level.
  • Not restoring mocks: Forgetting mockRestore() or afterEach(() => vi.restoreAllMocks()) leaks mock state between tests, causing flaky failures.
  • Mocking pure functions: If a function has no side effects and is fast, call it for real. Mocking it adds complexity without value.

Quick Reference

ScenarioStrategy
External API callMock the HTTP client
Database queryMock the repository/query function
Time-dependent logicFake timers (vi.useFakeTimers())
Error pathsmockRejectedValue / side_effect
Pure functionsDo NOT mock — test with real input
Internal utilitiesDo NOT mock — call for real
File I/OMock 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.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,852. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.