Раздел 29 · Тестирование

Фикстуры и тестовые данные: factory вместо god-объекта

middle~40 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Фикстуры и тестовые данные: factory вместо god-объекта

Сцена · общий реквизит на съёмках

Представь съёмочную площадку, где все сцены делят один реквизит. Покрасил стул для одной сцены, а переснимать пришлось десять, потому что во всех кадрах был этот же стул. Так же ведёт себя фикстура, когда она одна на весь набор: меняешь одно поле, и краснеет половина тестов, которые на это поле даже не смотрели.

В этом уроке разберёмся, как готовить тестовые данные так, чтобы каждый тест зависел только от того, что ему важно, а остальное пряталось в разумных дефолтах.

Карта урока · что заберёшь

  • поймёшь, чем хрупкая god-фикстура опасна для набора;
  • научишься писать factory с дефолтами и overrides;
  • узнаешь, когда нужен builder, а когда хватает factory;
  • заведёшь фабрики данных для домена Kassa.

Раздел 1 · почему общий объект хрупок

Идея словами

God-фикстура это соблазн новичка: один раз собрал большой объект счёта со всеми полями, и переиспользуй везде. Пока форма не меняется, всё хорошо. Но стоит добавить обязательное поле, как краснеют тесты, которым это поле безразлично. Тест должен падать по своей причине, а не из-за чужого изменения.

Картинка · сломай одно поле

Поле не менялось, все тесты зелёные.
God-фикстура0 красных
  • списывает сумму
  • пишет операцию в историю
  • отвергает платёж сверх остатка
  • считает остаток по истории
  • валидирует валюту счёта
Factory с overrides0 красных
  • списывает сумму
  • пишет операцию в историю
  • отвергает платёж сверх остатка
  • считает остаток по истории
  • валидирует валюту счёта

Нажми кнопку: мы добавляем в объект счёта одно обязательное поле. Слева god-фикстура роняет четыре теста, которые на это поле не смотрят. Справа те же тесты на factory остаются зелёными: новое поле спряталось в дефолтах фабрики, и менять пришлось одно место, а не каждый тест.

Что взять с собой

  • god-фикстура связывает тесты, которые не должны быть связаны;
  • одно изменение формы роняет половину набора;
  • тест должен падать по своей причине.

Раздел 2 · factory с дефолтами и overrides

Идея словами

Factory переворачивает зависимость. Вместо “один объект на всех” получается “функция, которая делает объект, и каждый тест просит только то, что ему нужно”. Релевантное поле на виду, остальное в дефолтах.

Шаг 1 · фабрика счёта и платежа

let seq = 0;

export const makeAccount = (overrides: Partial<Account> = {}): Account => {
  seq += 1;
  return {
    id: `acc_${seq}` as Account['id'],
    openingBalance: 100 as Money,
    balance: 100 as Money,
    history: [],
    ...overrides,
  };
};

export const makePayment = (amount: number, overrides: Partial<Payment> = {}): Payment => ({
  amount: amount as Money,
  memo: 'test payment',
  ...overrides,
});

Счётчик seq даёт уникальный id без ручной возни. В тесте видно только важное: makeAccount({ balance: 0 }) кричит “здесь важен нулевой остаток”, а всё прочее не отвлекает.

Шаг 2 · тест читается как одно предложение

it('отвергает платёж сверх остатка', () => {
  const reasons = validatePayment(makeAccount({ balance: 50 }), makePayment(100));
  expect(reasons).toContain('insufficient-funds');
});

Тест говорит ровно про свой случай. Добавь завтра в Account новое поле, и поправить нужно будет одну фабрику, а не двадцать тестов.

Когда код пишет агент

Агент по умолчанию копипастит полный объект в каждый тест: так быстрее сгенерировать. Получается набор, где двадцать почти одинаковых объектов разъезжаются при первом же изменении схемы. Попроси агента “вынеси создание данных в factory с overrides”, и набор станет устойчивым. Это ровно та работа, которую человек должен заказать явно.

Что взять с собой

  • factory отдаёт объект с дефолтами и принимает overrides;
  • в тесте на виду только релевантное поле;
  • изменение схемы правится в одном месте.

дальше после входа

Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.

Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.