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

> Пустой тест — это тестовый метод, тело которого не содержит ни одной исполняемой инструкции, поэтому он всегда проходит, ничего при этом не проверяя.

## Signs and Symptoms

Тест существует и числится как **пройденный**, но его тело не содержит исполняемого кода — ни подготовки, ни вызова, ни проверки. Характерные формы:

```js
// Полностью пустое тело
test('calculates tax', () => {});

// Только TODO / комментарий-заглушка
it('should reject invalid input', () => {
  // TODO: написать, когда стабилизируется API
});

// Всё закомментировано
it('parses the auth header', () => {
  // const result = parse(header);
  // expect(result).toEqual(expected);
});

```

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

* Имя теста обещает поведение, но тело — это `{}`, пробелы или одни лишь комментарии.
* Сводка раннера показывает тест как **пройденный** (зелёный), а не пропущенный/отложенный — именно это и делает его опасным.
* Покрытие кода для названной функциональности подозрительно нулевое, хотя «тест для неё» существует.
* При ревью диф добавляет тест-кейс, но ни одного вызова `expect`/`assert`.

Похожий, но иной случай: тест, у которого есть подготовка и вызов тестируемой системы, но **нет ни одной проверки**, — это запах _Тест без проверок / Неизвестный тест_. Пустой тест — более крайний случай, когда тело вообще лишено инструкций.

## Reasons for the Problem

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

* Тест создали как заготовку (в TDD — «сначала напиши имя») и так и не дописали.
* Код закомментировали, чтобы отладить падение или унять флакающий тест, а потом закоммитили и забыли.
* Генератор или IDE выдали скелет-заготовку теста, которую никто не доделал.
* Разработчик хотел «зарезервировать» имя теста на будущее, но вместо явного todo-маркера оставил пустое тело.

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

* **Ложная уверенность (самое худшее).** Большинство раннеров сообщают о пустом тесте как о _пройденном_, а не пропущенном. JUnit, Jest, Vitest и им подобные покажут зелёный для `test('x', () => {})`. Кажется, что набор тестов покрывает поведение, но регрессия в продакшен-коде так и не будет поймана — это, можно сказать, хуже, чем вообще не иметь теста, ведь зелёная галочка активно вводит в заблуждение.
* **Читаемость.** Читатель видит тест с именем `should reject invalid input` и резонно полагает, что это поведение проверено. Имя документирует намерение, которого тело так и не выполняет.
* **Сопровождаемость.** Пустые тесты раздувают число тестов и создают видимость покрытия «по именам», скрывая настоящие пробелы и мешая отличить намеренные заготовки от заброшенных.
* **Подрывает доверие.** Стоит участникам команды понять, что «пройден» не значит «проверен», и значимость сигнала всего набора тестов падает.

## Treatment

Решите, должен ли тест _вообще пока существовать_, а затем заставьте раннер говорить правду.

**1\. Если поведение нужно тестировать уже сейчас — заполните тело.** Добавьте arrange/act/assert, чтобы тест действительно выполнял код:

```js
// До — зелёный, но ничего не проверяет
test('rounds half up', () => {});

// После — настоящая проверка
test('rounds half up', () => {
  expect(round(2.5)).toBe(3);
});

```

**2\. Если это настоящая заготовка — пометьте её явно как отложенную**, чтобы раннер сообщал о ней как о todo/пропущенной (видимой, а не обманчиво зелёной), вместо того чтобы оставлять пустое тело:

```js
// До
it('should reject invalid input', () => {
  // TODO
});

// После — отображается как todo в отчёте, никогда как "пройден"
it.todo('should reject invalid input');   // Jest / Vitest
// или test.skip(...) / xit(...) с заведённым тикетом

```

**3\. Если тест устарел — удалите его.** Удалённый тест честен; пустой — это ложь, требующая сопровождения.

**4\. Восстановите закомментированную логику.** Если тело полностью закомментировано, либо раскомментируйте и почините его, либо удалите тест; никогда не оставляйте закомментированный тестовый код в роли «реализации».

**5\. Поставьте заслон.** Включите в CI правило линтера, требующее наличия проверок (см. детекторы), чтобы пустые тесты и тесты без проверок роняли сборку, а не проходили молча.

## Detected by

- **eslint-jest** `expect-expect` — eslint-plugin-jest: expect-expect (помечает тесты без проверок, включая пустые тела) (https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/expect-expect.md)
- **eslint-vitest** `expect-expect` — eslint-plugin-vitest: expect-expect (требует хотя бы одну проверку в каждом тесте) (https://github.com/vitest-dev/eslint-plugin-vitest/blob/main/docs/rules/expect-expect.md)
- **eslint** `no-empty-function` — ESLint core: no-empty-function (общее правило; помечает пустое тело стрелочной/обычной функции в колбэке пустого теста) (https://eslint.org/docs/latest/rules/no-empty-function)
- **sonar** `S2699` — SonarSource: S2699 «Тесты должны содержать проверки» (Blocker; помечает тесты без проверок и пустые тесты; есть аналоги для Java/C#/Python) (https://rules.sonarsource.com/javascript/RSPEC-2699/)
- **pmd** `JUnitTestsShouldIncludeAssert` — PMD (Java): JUnitTestsShouldIncludeAssert (помечает тесты JUnit без проверок, включая пустые тесты) (https://pmd.github.io/pmd/pmd_rules_java_bestpractices.html#junittestsshouldincludeassert)
