Признаки хороших тестов

Урок собирает свойства, которые отличают надёжный тест от ненадёжного.

Хороший тест быстрый, изолированный, детерминированный и читаемый — ему можно доверять и его не страшно запускать часто.

Принципы FIRST

Удобная мнемоника качеств теста:

БукваСвойство
F — Fastбыстрый: тысячи тестов за секунды
I — Isolatedизолированный: не зависит от других
R — Repeatableповторяемый: тот же результат при каждом прогоне
S — Self-validatingсам решает pass/fail без ручной проверки
T — Timelyпишется вовремя, рядом с кодом

Быстрый

Медленные тесты не запускают часто, а значит они бесполезны как ранняя обратная связь. Скорость даёт изоляция: без реальной сети, диска и setTimeout (используют моки и фейковые таймеры).

Детерминированный

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

function makeId() {
  return Math.floor(Math.random() * 1000);
}

// недетерминированная "проверка": результат разный каждый раз
const id = makeId();
console.log('сгенерирован id:', id);
console.log('Можно ли утверждать id === 42?', id === 42);

// детерминированный подход: проверяем СВОЙСТВО, а не точное значение
console.log('id в диапазоне [0, 999]:', id >= 0 && id < 1000);

Вывод:

сгенерирован id: 731
Можно ли утверждать id === 42? false
id в диапазоне [0, 999]: true

Точное значение случайно (у вас число будет другим), но свойство «в диапазоне [0, 999]» — детерминированно и всегда истинно. Вместо привязки к конкретному случайному числу либо мокают источник случайности, либо проверяют инвариант.

Читаемый

Тест читают чаще, чем пишут. Понятное имя (что проверяем), структура AAA, минимум лишнего — всё это делает тест документацией. Если по упавшему тесту нельзя понять, что сломалось, тест плохой.

Самопроверяемый

Тест должен сам однозначно сказать pass/fail. Никаких «посмотрите в консоль глазами» — для этого есть expect.

Итог

  • FIRST: Fast, Isolated, Repeatable, Self-validating, Timely.
  • Скорость и изоляцию дают моки и фейковые таймеры.
  • Детерминизм: убирайте время, случайность и порядок; проверяйте инварианты.
  • Читаемый самопроверяемый тест — это живая документация.
Проверьте себя
1. Что означает буква I в принципах FIRST?
AIntegration — интеграционный
BIsolated — изолированный, не зависит от других тестов
CImportant — важный
DIndexed — проиндексированный
2. Как сделать детерминированной проверку функции, возвращающей случайный id из диапазона?
AСравнивать с конкретным числом вроде 42
BПроверять инвариант (например, что id в диапазоне) или мокать источник случайности
CЗапускать тест много раз
DУдалить тест
3. Почему важна скорость тестов?
AБыстрые тесты красивее
BМедленные тесты редко запускают, и они теряют ценность ранней обратной связи
CСкорость влияет на покрытие
Dnpm требует быстрых тестов
Поддержать проект