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

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

## Signs and Symptoms

Вы не можете с первого взгляда сказать, что доказывает тест, хотя ничего хитрого не происходит — замысел тонет в объёме. Типичные приметы:

* Тестовый метод **длинный** (каталог Test Smells использует грубый порог в **\>30 строк**) или не помещается на один экран.
* Большой блок `arrange` строит объекты поле-за-полем, и многое из этого нерелевантно проверяемому поведению.
* Множество **захардкоженных литералов** без очевидной связи между входами и проверяемыми выходами.
* Результат проверяется **грудой проверок отдельных полей** вместо одного осмысленного сравнения.
* Читая проверки, вы не можете легко сопоставить, какой вход вызвал какое ожидаемое значение.

```js
test('places an order for a returning customer', () => {
  const address = new Address();
  address.street = '123 Main St';
  address.city = 'Springfield';
  address.zip = '00000';                          // нерелевантно этому тесту
  address.country = 'US';

  const customer = new Customer();
  customer.id = 42;
  customer.firstName = 'Ada';
  customer.lastName = 'Lovelace';
  customer.email = 'ada@example.com';
  customer.address = address;
  customer.createdAt = new Date('2020-01-01');     // шум

  const product = new Product();
  product.sku = 'SKU-1';
  product.name = 'Widget';
  product.price = 9.99;

  const cart = new Cart();
  cart.add(product, 2);

  const order = orderService.placeOrder(customer, cart);

  expect(order.status).toBe('CONFIRMED');          // единственные значимые строки
  expect(order.lineItems.length).toBe(1);
  expect(order.lineItems[0].sku).toBe('SKU-1');
  expect(order.lineItems[0].quantity).toBe(2);
  expect(order.total).toBe(19.98);
  expect(order.customerId).toBe(42);
});

```

Около 25 строк конструирования охраняют двухстрочную проверку итогов и статуса.

## Reasons for the Problem

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

* _Inline-конструирование с обязательными полями._ Создание реальных объектов через конструкторы/сеттеры вынуждает поставлять данные, до которых тесту нет дела, только ради того, чтобы код скомпилировался и запустился. Мезарос отмечает, что уровень детализации, нужный для исполняемости теста, может сделать его «настолько многословным, что его трудно понять».
* _Рост через копипаст._ Тест начинается маленьким, затем каждый новый сценарий создаётся копированием предыдущего и подкручиванием значения, так что шум накапливается.
* _Нет общих хелперов конструирования._ Без Creation Methods, билдеров или фикстур каждый тест заново заявляет весь граф объектов.
* _Захардкоженные данные и проверка поле-за-полем._ Писать литералы повсюду и проверять каждое свойство кажется «основательным», но многократно раздувает число строк.

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

* _Читаемость._ Многословный тест — это разновидность **Obscure Test** — каталог даже указывает «Obscure Test» как его псевдоним. Читатель не видит связки «причина-следствие» между фикстурой и исходом, а ведь в этом весь смысл теста, основанного на примере.
* _Сопровождаемость._ Тест в 30+ строк обычно несёт «несколько обязанностей», поэтому небольшое изменение в продакшене расходится по многим строкам во многих тестах. Массовая inline-подготовка к тому же плодит дублирование.
* _Ложная уверенность._ Длинные, шумные тесты прячут баги на виду: неверный литерал или сбившаяся проверка легко проскальзывают, а многословие заставляет ревьюеров скользить взглядом. Объём выглядит строгим, ничего при этом не доказывая дополнительно.
* _Надёжность._ Чем больше нерелевантного состояния фиксирует тест, тем вероятнее он сломается по причинам, не связанным с его замыслом (избыточная специализация), давая хрупкие, малосодержательные падения.

## Treatment

Вытолкните шум из тела теста, чтобы на виду остались только те переменные, что определяют поведение. Конкретные приёмы (все из xUnit Test Patterns):

1. **Извлеките Creation Methods / используйте билдер.** Замените inline-конструирование объектов выразительно названным хелпером, который поставляет разумные значения по умолчанию; передавайте только те значения, которые тесту действительно важны (Parameterized Creation Method). Это убивает и шаблонный код, и Irrelevant Information.
2. **Задавайте нерелевантные данные по умолчанию внутри хелпера**, сигнализируя «эти значения не влияют на исход».
3. **Замените проверки поле-за-полем на Expected Object** (`toEqual`/`toMatchObject`) или **Custom Assertion / метод верификации**, который называет проверяемое понятие.
4. **Проверяйте одно поведение на тест.** Если один тест длинен потому, что прогоняет несколько исходов (Eager Test), разбейте его.
5. **Параметризуйте** почти идентичные многословные тесты через `test.each` / `it.each` вместо копипаста.

```js
// до — см. «Признаки и симптомы» (≈30 строк подготовки + 6 проверок)

// после
test('confirms the order and charges line-item total', () => {
  const customer = aCustomer();                          // Creation Method, значения по умолчанию
  const cart = aCartWith(aProduct({ price: 9.99 }), 2);  // только релевантный вход

  const order = orderService.placeOrder(customer, cart);

  expect(order).toMatchObject({                          // Expected Object, а не поле-за-полем
    status: 'CONFIRMED',
    total: 19.98,
  });
});

```

Сценарий теперь читается за секунды: покупатель берёт две единицы товара за $9.99 → заказ подтверждён с итогом $19.98\. Хелперы (`aCustomer`, `aProduct`, `aCartWith`) — это общие, протестированные утилиты, поэтому шум живёт в одном месте, а не в каждом тесте.

Страховка: ограничьте длину тестовой функции и число проверок в линтере (см. детекторы), чтобы тесты не могли тихо разрастись обратно в многословные.

## Detected by

- **eslint** `max-lines-per-function` — Ограничивает максимальное число строк в функции — помечает тела тестов, превышающие заданный лимит, ключевую меру многословного теста. (https://eslint.org/docs/latest/rules/max-lines-per-function)
- **eslint** `max-statements` — Ограничивает максимальное число операторов в блоке функции — лимитирует то, сколько может делать один тест (стрелочный/функциональный коллбэк). (https://eslint.org/docs/latest/rules/max-statements)
- **sonar** `S138` — Функции не должны содержать слишком много строк кода — применяется к тестовым функциям; помечает раздутые, многозадачные тестовые методы (RSPEC-138). (https://rules.sonarsource.com/javascript/RSPEC-138/)
- **eslint-jest** `jest/max-expects` — Ограничивает максимальное число вызовов expect() на тест (по умолчанию 5) — ловит грань нагромождения проверок в многословных/жадных тестах. (https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/max-expects.md)
- **eslint-vitest** `vitest/max-expects` — Ограничивает максимальное число вызовов expect() на тест (по умолчанию 5) — эквивалент jest/max-expects для Vitest. (https://github.com/vitest-dev/eslint-plugin-vitest/blob/main/docs/rules/max-expects.md)
