Антипаттерны и флаки

Урок разбирает типичные ошибки в тестах и нестабильные «флаки»-тесты.

Флаки-тест (flaky) — тест, который на одном и том же коде иногда проходит, а иногда падает; он подрывает доверие ко всему набору.

Антипаттерн 1: тест на реализацию

Тест проверяет как код устроен внутри, а не что он делает. Признак — тест ломается при рефакторинге, который не меняет поведение. Лекарство: проверять вход → выход и наблюдаемые эффекты, а не приватные детали (см. урок «Что именно тестировать»).

Антипаттерн 2: флаки-тесты

Главные причины нестабильности:

  • Реальное время и таймеры — гонки, зависимость от скорости машины. Лечится фейковыми таймерами.
  • СлучайностьMath.random, случайные данные. Лечится моком или проверкой инварианта.
  • Зависимость от порядка — общее состояние между тестами. Лечится изоляцией и beforeEach.
  • Реальная сеть — таймауты, недоступность. Лечится моками.

Флаки опаснее честно красного теста: команда привыкает «просто перезапустить» и в итоге игнорирует и настоящие падения.

Антипаттерн 3: тест без проверки (ложная зелень)

Тест есть, но в нём нет реального expect — он всегда зелёный и ничего не гарантирует. Покажем, чем это опасно:

function divide(a, b) {
  return a / b; // баг: нет защиты от деления на 0
}

// ПЛОХОЙ "тест": вызвали, но ничего не проверили
function badTest() {
  divide(10, 0); // результат проигнорирован
  console.log('\u2713 badTest "прошёл" (но ничего не проверил!)');
}

// ХОРОШИЙ тест: есть проверка
function goodTest() {
  const result = divide(10, 0);
  const ok = Number.isFinite(result);
  console.log((ok ? '\u2713 passed' : '\u2717 FAILED') +
    ': divide(10,0) даёт конечное число (получили ' + result + ')');
}

badTest();
goodTest();

Вывод:

✓ badTest "прошёл" (но ничего не проверил!)
✗ FAILED: divide(10,0) даёт конечное число (получили Infinity)

Плохой тест зелёный и бесполезен. Хороший — ловит реальный баг (Infinity). Всегда добавляйте осмысленный expect.

Антипаттерн 4: хрупкость и переусложнение

  • Логика в тесте (циклы, условия, вычисление ожидаемого) — тест сам может содержать баг. Ожидаемые значения лучше писать явно.
  • Слишком много моков — тест проверяет моки, а не код. Если всё замокано, что вообще тестируется?

Итог

  • Не тестируйте реализацию — только поведение.
  • Флаки чаще всего от времени, случайности, порядка и сети; убирайте их источники.
  • Тест без expect — ложная зелень, бесполезен.
  • Избегайте логики в тестах и чрезмерного мокинга.
Проверьте себя
1. Что такое флаки-тест?
AОчень быстрый тест
BТест, который на одном и том же коде то проходит, то падает
CТест без описания
DТест с покрытием 100%
2. Чем опасен тест без реального expect?
AОн слишком медленный
BОн всегда зелёный и ничего не гарантирует, маскируя баги
CОн ломает покрытие
DОн требует сети
3. Какой источник чаще всего делает тест флаки?
AИспользование describe
BРеальное время, случайность, порядок выполнения и сеть
CНаличие комментариев
DСлишком короткое имя теста
Поддержать проект