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