---
title: "Универсальная фикстура"
type: "test-smell"
slug: "general-fixture"
url: "http://localhost:3000/ru/test-smells/general-fixture.md"
category: "Запахи фикстур"
description: "Общая подготовка строит одну большую фикстуру «на все случаи жизни», покрывающую потребности всех тестов, но каждый отдельный тест задействует лишь небольшую её часть."
---
# Универсальная фикстура

> Общая подготовка строит одну большую фикстуру «на все случаи жизни», покрывающую потребности всех тестов, но каждый отдельный тест задействует лишь небольшую её часть.

## Signs and Symptoms

Единственный `beforeEach`/`setUp` (или общая подготовка на уровне модуля) конструирует кучу объектов, засевает много записей и поднимает несколько моков — а любой отдельный тест трогает лишь один-два из них. Эвристика обнаружения из каталога тестовых запахов проста: _не все поля, создаваемые в подготовке, используются всеми тестовыми методами_.

Как это распознать:

* Блок подготовки длинный и разрастается с каждым новым тестом.
* Чтобы понять один тест, приходится прокручивать вверх и реверс-инжинирить, какие части фикстуры ему на самом деле важны.
* Многие тесты падают или требуют правок, когда вы меняете один общий объект, до которого большинству из них и дела нет.
* Одна и та же фикстура из одного места обслуживает совершенно разные сценарии (создание, валидация, оплата, права доступа).

```js
// Запах: одна фикстура на всё; каждый тест использует от неё кусочек
let user, admin, product, cart, order, paymentGateway, shippingZone;

beforeEach(() => {
  user            = createUser({ name: 'Ada' });
  admin           = createAdmin();
  product         = createProduct({ price: 10 });
  cart            = createCart(user, [product]);
  order           = createOrder(cart);
  paymentGateway  = mockGateway();
  shippingZone    = createZone('EU');
});

test('cart subtotal sums its line items', () => {
  // нужны только cart + product, но платит за admin, order, gateway, zone…
  expect(cart.subtotal()).toBe(10);
});

```

## Reasons for the Problem

**Почему это происходит**

* **Слишком агрессивно применённый DRY.** Подготовку сводят в один общий хук, чтобы «избежать дублирования», и потребности каждого нового теста прикручивают к той же фикстуре.
* **Органический рост.** Фикстура обрастает объектами по мере добавления тестов, и никто не вычищает то, что старые тесты больше не используют.
* **Инерция неявной подготовки.** Когда большой `beforeEach` уже есть, добавить ещё одну строку проще, чем создавать узкоспециализированную фикстуру для нового случая.

**Чем это вредит**

* **Читаемость / тесты-как-документация.** Тест должен читаться как ясная связь причина→следствие. Когда большая часть фикстуры — несущественный шум, читатель не может понять, что на самом деле определяет результат. Противоядие Месароша, _минимальная фикстура_, существует именно потому, что тест с наименьшей фикстурой всегда легче понять.
* **Сопровождаемость.** Изменение, которого требует один тест (например, дать `product` новое обязательное поле), вынуждает править подготовку, общую для всех тестов, порождая каскадные поломки и связывая друг с другом не связанные тесты.
* **Надёжность.** Общее, изменяемое состояние фикстуры позволяет тестам влиять друг на друга и затрудняет локализацию падений — классический источник зависящего от порядка флакования.
* **Скорость.** Каждый тест оплачивает полную стоимость построения всей фикстуры. Универсальная фикстура числится среди причин медленных тестов.
* **Ложная уверенность.** Тесты оказываются переспецифицированы под случайные детали фикстуры, поэтому проходят или падают по причинам, не связанным с проверяемым поведением.

## Treatment

Стремитесь к **минимальной фикстуре**: каждый тест готовит только то, что нужно именно ему, и ничего больше.

1. **Проведите инвентаризацию использования.** Для каждого общего поля выпишите, какие тесты его действительно читают. Всё, что используется лишь подмножеством, — кандидат на вынос из общей подготовки.
2. **Перенесите конструирование в создающие методы / билдеры тестовых данных (делегированная подготовка).** Сохраняйте лаконичность тестов, не навязывая одну гигантскую фикстуру, — каждый тест вызывает билдер и переопределяет только важные ему атрибуты.
3. **Предпочитайте свежую фикстуру на каждый тест** долгоживущей общей, устраняя межтестовую связанность и изменяемое общее состояние.
4. **Разделяйте по сценариям.** Если группы тестов действительно делят _небольшую_ фикстуру, разнесите их по узким блокам `describe` (или тестовым классам), каждый со своей минимальной подготовкой.
5. **Оставляйте в `beforeEach` только то, что действительно общее и небольшое;** остальное переносите прямо в тест, чтобы причина и следствие были рядом с проверкой.

```js
// До: универсальная фикстура в общем хуке (см. «Признаки и симптомы»)

// После: минимальная, делегированная подготовка — каждый тест строит только нужное
test('cart subtotal sums its line items', () => {
  const cart = aCart().withItem(aProduct().price(10)).build();
  expect(cart.subtotal()).toBe(10);
});

test('checkout charges the payment gateway', () => {
  const gateway = mockGateway();
  const cart    = aCart().withItem(aProduct().price(10)).build();

  checkout(cart, gateway);

  expect(gateway.charge).toHaveBeenCalledWith(10);
});

```

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