NODEJS Contents

Mocking Strategy

Mocking is useful when dependencies are slow or non-deterministic, but over-mocking creates false confidence; mock at boundaries, not everywhere.

On this page

What Mocking Is For

Mocking replaces a real dependency with a controlled fake so tests are fast and deterministic. This is valuable for external services (payments, email, SMS), time-dependent logic, and failure simulations that are hard to reproduce reliably.

Mock at the Boundary

Mock your service clients and repositories, not your internal functions. If you mock internal implementation details, your tests will pass even when the behavior breaks.

Example: Mock a Repository Interface

type User = { id: string; email: string };

interface UserRepo {
  findByEmail(email: string): Promise;
}

async function registerUser(repo: UserRepo, email: string) {
  const existing = await repo.findByEmail(email);
  if (existing) throw new Error('Email already used');
  return { id: 'new', email };
}

Test With a Fake

import test from 'node:test';
import assert from 'node:assert/strict';

test('rejects duplicate email', async () => {
  const repo = { findByEmail: async () => ({ id: '1', email: 'a@b.com' }) };
  await assert.rejects(() => registerUser(repo, 'a@b.com'), /Email already used/);
});

Mocking Failure Modes

Mocks are especially valuable for simulating timeouts, retries, 429 rate limits, and partial outages. These are real production conditions that often go untested.

Over-Mocking Smells

  • You mock 10 things just to test one route
  • Tests assert internal call order instead of response behavior
  • Refactors break tests even though behavior is unchanged