Игнорируемый тест.
Тест, который закоммичен в кодовую базу, но никогда не выполняется, потому что он пропущен, отключён или закомментирован, создавая видимость покрытия, ничего на самом деле не проверяя.
##Signs and Symptoms
Тест существует в наборе, но навсегда лишён возможности выполняться. Он по-прежнему считается «тестом» в файле, однако даёт ноль проверки. Высматривайте маркеры skip/ignore, блоки с префиксом x, пустые тела и код тестов, погребённый в комментариях, — нередко в паре с расплывчатой отговоркой вроде «flaky» или «fix later».
// модификаторы skip/disabled — никогда не выполняются
describe.skip('checkout flow', () => { /* ... */ });
it.skip('applies the discount', () => { /* ... */ });
test.todo('handles expired coupons');
// с префиксом x (jasmine/jest/mocha) — тот же эффект
xit('rejects negative amounts', () => { /* ... */ });
xdescribe('payments', () => { /* ... */ });
// закомментированный тест — невидим И для раннера, И для репортёра
// it('retries on 503', async () => {
// await expect(client.fetch()).resolves.toBeOk();
// });
// JUnit: @Ignore / @Disabled с расплывчатой причиной
@Ignore("disabled for now as this test is too flaky")
@Test public void peerPriority() { /* ... */ }
Выдающие признаки в отчёте о тестах: ненулевой счётчик «skipped»/«pending», на который никто не смотрит; наборы, тихо съёжившиеся со временем; и пропуски без привязанной задачи или срока истечения. Условные return в начале теста (if (process.platform === 'win32') return;) — более хитрый вариант, полностью скрывающий пропуск от счётчиков пропусков.
##Reasons for the Problem
Почему это происходит
- Тест начал падать (настоящая регрессия, нестабильная зависимость от времени, смена среды/версии), и пропуск был самым быстрым способом получить зелёную сборку или разблокировать слияние.
- Тест был написан раньше реализации (
test.todo/xitкак заглушка) и к нему так и не вернулись. - Миграции (новый фреймворк, среда выполнения или API) сломали тест, и его припарковали «временно».
- Пропуск задумывался на полдня; без напоминания, срока истечения или отслеживающей задачи он становится постоянным.
Почему это вредно
- Ложная уверенность. Пропущенный тест выглядит как покрытие в файле и в diff пул-реквеста, но ничего не проверяет. Путь кода, который он якобы защищал, теперь без охраны, а все считают его безопасным.
- Гниение. Игнорируемый тест никогда не компилируется (в некоторых языках), не рефакторится и не обновляется. Чем дольше он лежит, тем сильнее отходит от реальности, пока его повторное включение не станет дороже переписывания.
- Скрытые регрессии. Баг или нестабильное поведение, спровоцировавшее пропуск, всё ещё на месте — его просто заглушили. Пропуск лечит симптом (красную полосу), а не болезнь.
- Шум и эрозия. Постоянные счётчики «пропущено» приучают команду игнорировать отчёт о тестах, из-за чего новые пропуски проскальзывают незамеченными. Мёртвый и закомментированный код тестов также добавляет издержки на чтение и сопровождение без всякой пользы.
##Treatment
Относитесь к каждому игнорируемому тесту как к решению, которое нужно принять сейчас, а не откладывать бесконечно.
- Разберите каждый пропуск. Для каждого отключённого/закомментированного/
todo-теста решите: починить, удалить или поместить в карантин со сроком истечения и отслеживаемой задачей. «Оставить пропущенным навсегда» — не вариант. - Почините и снова включите, если поведение всё ещё важно. Если тест нестабилен, устраните нестабильность (возьмите под контроль время, случайность, асинхронность и общее состояние), а не пропускайте его.
- Удалите его, если функциональность исчезла или тест устарел. Удалённый тест честен; пропущенный лжёт. Система контроля версий помнит его, если он когда-нибудь понадобится, — поэтому никогда не комментируйте тест вместо его удаления.
- Если приходится пропустить временно, сделайте это громко и с ограничением по времени: всегда указывайте причину и ссылку на отслеживание, и предпочитайте механизм, который проявляется в отчётах и падает по истечении срока, чтобы пропуск не сгнил.
- Остановите кровотечение с помощью линтера/CI. Включите правило «никаких отключённых тестов», чтобы новые пропуски ловились на ревью, и относитесь к существующему счётчику пропусков как к бэклогу, который нужно свести к нулю.
// до — тихо, навсегда, без отслеживания
it.skip('refunds the full amount on cancel', async () => {
await expect(refund(order)).resolves.toEqual({ amount: 100 });
});
// после — починен и снова работает (коренная причина: итог с плавающей точкой)
it('refunds the full amount on cancel', async () => {
await expect(refund(order)).resolves.toEqual({ amount: 100 });
});
// допустимый промежуточный вариант — заметный, с указанием автора и сроком истечения
it.skip('refunds the full amount on cancel — flaky clock, see JIRA-1234 (remove by 2026-07-01)', async () => {
/* ... */
});
// до
@Ignore("too flaky")
@Test public void peerPriority() { ... }
// после: почините зависимость от тайминга и снова включите, или удалите, если устарел
@Test public void peerPriority() { ... }
##Detected by
- eslint-jest jest/no-disabled-tests — Запретить отключённые тесты
- eslint-jest jest/no-commented-out-tests — Запретить закомментированные тесты
- eslint-vitest vitest/no-disabled-tests — Запретить отключённые тесты
- eslint-vitest vitest/no-commented-out-tests — Запретить закомментированные тесты
- sonar java:S1607 — Тесты не должны игнорироваться
- sonar javascript:S1607 — Тесты не должны пропускаться без указания причины