ConstructiCat Logo
CodeBust.
Browse section ▾

Пустой тест.

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

##Signs and Symptoms

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

// Полностью пустое тело
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, чтобы тест действительно выполнял код:

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

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

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

// До
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-expecteslint-plugin-jest: expect-expect (помечает тесты без проверок, включая пустые тела)
  • eslint-vitest expect-expecteslint-plugin-vitest: expect-expect (требует хотя бы одну проверку в каждом тесте)
  • eslint no-empty-functionESLint core: no-empty-function (общее правило; помечает пустое тело стрелочной/обычной функции в колбэке пустого теста)
  • sonar S2699SonarSource: S2699 «Тесты должны содержать проверки» (Blocker; помечает тесты без проверок и пустые тесты; есть аналоги для Java/C#/Python)
  • pmd JUnitTestsShouldIncludeAssertPMD (Java): JUnitTestsShouldIncludeAssert (помечает тесты JUnit без проверок, включая пустые тесты)