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

> Нетерпеливый тест проверяет несколько разных методов или поведений тестируемого модуля в одном тестовом методе вместо того, чтобы сосредоточиться на одном поведении.

## Signs and Symptoms

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

Характерные признаки:

* Расплывчатое, всеохватное имя теста (`testUserService`, `it('works')`) или сшитое из «и» (`creates_and_renames_and_deletes`).
* Несколько циклов **действие → проверка** в одном теле: вы вызываете метод, проверяете, вызываете другой, проверяете снова.
* Комментарии, используемые как заголовки секций внутри теста (`// now test delete`), чтобы разделить проверяемые вещи.
* Проверки затрагивают несколько разных методов/полей SUT, не имеющих общего логического результата.

```js
// ЗАПАХ: один тест проверяет create, rename, delete и list
test('user service', () => {
  const svc = new UserService();

  const user = svc.create({ name: 'Ada' });   // поведение 1
  expect(user.id).toBeDefined();

  svc.rename(user.id, 'Grace');                // поведение 2
  expect(svc.get(user.id).name).toBe('Grace');

  svc.delete(user.id);                         // поведение 3
  expect(svc.get(user.id)).toBeUndefined();

  expect(svc.list()).toHaveLength(0);          // поведение 4
});

```

Обратите внимание на различие: нетерпеливый тест — это о проверке **нескольких поведений**, а не просто о наличии нескольких `expect`. Несколько проверок одного логического результата (например, проверка нескольких полей _одного и того же_ возвращённого объекта) — это нормально и не является этим запахом.

## Reasons for the Problem

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

* **Переиспользование подготовки / лень.** Подготовка фикстуры утомительна или дорога, поэтому соблазнительно продолжать тыкать в уже построенный объект и проверять «раз уж мы здесь».
* **Мышление сценариями.** Автор тестирует пользовательский сценарий («создать, затем отредактировать, затем удалить») как один скрипт вместо изоляции каждого отдельного поведения модуля.
* **Дрейф TDD.** Тест, начавшийся сфокусированным, накапливает лишние проверки по мере того, как новая функциональность прикручивается к нему, а не получает свой собственный тест.

**Почему это вредно**

* **Ложная уверенность (главное).** Большинство библиотек проверок прерывают тест на _первой_ упавшей проверке. Если `create` сломан, проверки `rename`, `delete` и `list` никогда не выполнятся — поэтому переход из зелёного в красный скрывает, сколько поведений на самом деле сломано, а проходивший тест на самом деле никогда не прогонял поздние шаги, как только регрессировал один из ранних.
* **Плохая диагностика.** Падение говорит вам «тест сервиса пользователей упал», а не _какое_ поведение. Чтобы найти сломанный шаг, приходится читать весь метод.
* **Затуманенное намерение / читаемость.** Тест больше не документирует один факт о системе; читателю приходится мысленно разбивать его на поведения, которые он связывает в пучок.
* **Хрупкость и связанность.** Поздние проверки зависят от ранних мутаций, поэтому несвязанное изменение в начале каскадирует и делает тест ломким и трудным для рефакторинга.
* **Сопровождаемость.** Труднее удалить, переместить или переименовать покрытие поведения, когда оно переплетено с тремя другими в одном методе.

## Treatment

Разбейте нетерпеливый тест на несколько сфокусированных тестов — **одно поведение на тест** — и поднимите общий шаг подготовки в setup, чтобы разбиение не дублировало шаблонный код.

Шаги:

1. **Перечислите поведения**, которые тест связывает в пучок (здесь: создание, переименование, удаление, список после удаления).
2. **Извлеките по одному тесту на поведение**, каждый с описательным, раскрывающим намерение именем.
3. **Перенесите общую подготовку** в `beforeEach` или фабрику/метод создания, чтобы каждый тест по-прежнему имел чистую SUT без скопированного кода подготовки.
4. **Оставьте по одному действию (Act)** на тест и проверяйте только результат этого поведения (несколько `expect` по _одному_ результату — это нормально).
5. Если вам действительно нужно проверить сквозной **сценарий**, оставьте его как один явно названный сценарный/интеграционный тест — но всё равно покрывайте каждое отдельное поведение собственным тестом, а не полагайтесь на сценарий ради покрытия.
6. **Защититесь от регрессий**, включив правило максимума проверок (см. детекторы) как дешёвый суррогат.

```js
// ПОСЛЕ: сфокусированные тесты, общая подготовка
let svc;
beforeEach(() => { svc = new UserService(); });

test('create() assigns an id', () => {
  expect(svc.create({ name: 'Ada' }).id).toBeDefined();
});

test('rename() updates the stored name', () => {
  const { id } = svc.create({ name: 'Ada' });
  svc.rename(id, 'Grace');
  expect(svc.get(id).name).toBe('Grace');
});

test('delete() removes the user', () => {
  const { id } = svc.create({ name: 'Ada' });
  svc.delete(id);
  expect(svc.get(id)).toBeUndefined();
});

```

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

## Detected by

- **eslint-jest** `jest/max-expects` — Ограничить максимальное число вызовов expect() на тест (https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/max-expects.md)
- **eslint-vitest** `vitest/max-expects` — Ограничить максимальное число expect на тест (https://github.com/vitest-dev/eslint-plugin-vitest/blob/main/docs/rules/max-expects.md)
