Skip to content
PПромтбук
RUEN
04Тестирование

Стратегия моков в тестах

Что мокать, что нет, и как не сделать тесты бесполезными от перемокания.

Спроектируй стратегию моков.

Главный принцип

Мокай зависимости которые дороги, медленны или непредсказуемы. Не мокай зависимости которые тестируешь.

Что МОКАТЬ всегда

  • Внешние API (Stripe, OpenAI, Twilio...)
  • Время (Date.now(), таймеры)
  • Случайность (Math.random(), UUID)
  • File system (зависит от уровня теста)
  • Сеть в unit-тестах

Что НЕ МОКАТЬ

  • Свои собственные функции (это уже не unit-тест функции, это тест моков)
  • Pure-логику
  • БД в integration-тестах (используй test-DB или транзакции с откатом)

Способы

A. Manual mock

const sendEmail = jest.fn().mockResolvedValue({ id: 'msg-1' });
// тест
await registerUser({ email: '...', sendEmail });
expect(sendEmail).toHaveBeenCalledWith({ to: '...', template: 'welcome' });

B. Mock module целиком

jest.mock('@/lib/email', () => ({
  sendEmail: jest.fn().mockResolvedValue({ id: 'msg-1' })
}));

C. MSW (Mock Service Worker) для HTTP

Перехватывает реальные fetch запросы на уровне сети. Тесты максимально близки к продакшен-коду.

server.use(
  http.post('https://api.stripe.com/v1/charges', () => {
    return HttpResponse.json({ id: 'ch_test', amount: 1000 });
  })
);

D. Test doubles по уровням

ТипЧто этоКогда
DummyЗаглушка, никогда не используетсяЗаполнить параметр
StubВозвращает фиксированное значениеПростой кейс
FakeРаботающая упрощённая реализация (in-memory DB)Integration
SpyStub + запоминает вызовыПроверить что вызвали
MockStub + проверки в концеПолный контракт

Anti-patterns

✗ Мокать внутренние функции того же модуля

// плохо — теперь не проверяешь логику
jest.spyOn(myModule, 'helper').mockReturnValue(...);

Перемокание — все зависимости моки, тестируется только сам мок

const user = { id: 1, name: 'Test', active: true };
expect(formatUser(user)).toBe(...); // что тут проверяется?

Mock data копирует prod — изменилась схема → 100 тестов сломались

Глобальное состояние моков — один тест меняет, другой ломается

Хорошие практики

Fakes для сложных зависимостей

class InMemoryUserRepo implements UserRepo {
  private users = new Map();
  async save(u) { this.users.set(u.id, u); }
  async find(id) { return this.users.get(id); }
}

Factories для тестовых данных

const buildUser = (overrides = {}) => ({
  id: 'u-1', email: 'test@test.com', active: true, ...overrides
});

Reset моков между тестами

beforeEach(() => jest.clearAllMocks());

Тест: "тесты ловят настоящие баги?"

Сломай намеренно одну строчку в проде-коде. Если тесты прошли — они моки тестируют, не код. Удали или переделай.

Принципы

  • Чем меньше моков — тем ближе к реальности
  • Fake (in-memory) часто лучше mock
  • MSW > monkey-patching fetch
  • Если приходится мокать 5 модулей чтобы протестировать один — этот один делает слишком много
К подразделу «Тестирование»
Похожие промты