JavaScript / 37 минут

Тестирование и итоговый проект

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

В процессе Теория: впереди Практика: впереди Тест: впереди
22 / 22
После этой главы
  • Пишите проверки поведения, различайте уровни тестов и соберите итоговый проект.
  • Применить тему в задаче «Тестирование и итоговый проект» и объяснить граничные случаи.

Около 7 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.

Тест как проверяемое обещание

Автоматический тест фиксирует наблюдаемое поведение программы на выбранном входе. Он не доказывает отсутствие всех ошибок, но делает важные ожидания повторяемыми. Хорошее название объясняет правило: «пустая история даёт серию ноль», а не просто «test 1». Начните с контракта и примеров, для которых результат можно независимо получить вручную.

Обычно тест разделяют на подготовку, действие и проверку. В подготовке создаются данные и зависимости, действие вызывает проверяемую единицу, проверка сравнивает фактическое с ожидаемым. Если для понимания теста нужно проследить большую общую цепочку скрытого состояния, он становится хрупким и плохо объясняет причину отказа.

JavaScript
import test from 'node:test';
import assert from 'node:assert/strict';
test('пустой список имеет сумму ноль', () => {
  const prices = [];
  const total = prices.reduce((sum, price) => sum + price, 0);
  assert.equal(total, 0);
});

Уровни проверки

Unit-тест проверяет небольшую единицу поведения с контролируемыми зависимостями. Интеграционный тест проверяет совместную работу частей: например, маршрут, парсер и сервис. Сквозной тест проходит пользовательский или системный сценарий через реальные границы. Эти уровни дополняют друг друга, а не соревнуются за звание единственного правильного подхода.

Для чистого расчёта серии занятий не нужен настоящий браузер и HTTP-сервер. Для проверки статуса и тела API недостаточно отдельно вызвать функцию форматирования ответа. Выбирайте границу теста по риску, который он должен обнаруживать. Не подменяйте все зависимости подряд так, что проверка начинает подтверждать только собственные настройки mock, а не рабочую связь компонентов.

Равенство и граничные случаи

Строгое равенство хорошо подходит примитивам и проверке идентичности. Для содержимого массива или обычного объекта нужна подходящая глубокая проверка. Выражение [1,2] === [1,2] ложно, потому что это разные объекты. При этом глубокое сравнение тоже должно учитывать контракт: порядок массива может быть существенным, а порядок ключей словаря — нет.

JavaScript
assert.deepEqual([1, 2], [1, 2]);
assert.notEqual({ id: 1 }, { id: 1 });
assert.equal(Number.isNaN(Number('x')), true);

Для каждой функции ищите границы: пустой набор, один элемент, минимально допустимое значение, переход через условие, дубликаты и ошибочный ввод. Набор тестов не обязан включать тысячи случайных примеров, если несколько тщательно выбранных проверяют разные правила. Важно понимать, какую ошибку способен поймать каждый тест.

Асинхронные тесты и стабильность

Тест должен дождаться проверяемой работы. Если запустить Promise и завершить тест раньше, ошибка может не попасть в правильную проверку или повлиять на следующий сценарий. Для ожидаемого отказа используйте API проверки rejected Promise, а не случайный try/catch, который сам может забыть подтвердить наличие ошибки.

JavaScript
test('ожидаемый асинхронный отказ', async () => {
  await assert.rejects(async () => {
    throw new Error('offline');
  }, /offline/);
});

Случайные значения, текущее время и настоящая внешняя сеть часто делают тесты нестабильными. Передавайте генератор времени или внешнюю зависимость явно и управляйте её ответом. Это не означает, что интеграции никогда не тестируют реально: просто такой тест имеет другую стоимость, окружение и ответственность. Фиксированная пауза не является надёжным доказательством завершения операции.

Рефакторинг и полезные assertions

Проверка количества приватных методов или точного текста внутренней функции часто мешает безопасному рефакторингу. Если публичное поведение не изменилось, тест не должен падать только из-за переноса вычисления в helper. Напротив, отсутствие проверки важного результата делает тест зелёным даже при сломанном поведении. Старайтесь утверждать содержимое ответа, сохранённое состояние и значимые эффекты.

В задаче этой главы нужно найти индексы различий двух массивов чисел. Здесь отдельно важна разная длина: отсутствующий элемент должен считаться различием. Если сравнивать только до длины первого массива, проверка не заметит лишние элементы второго. Пустой массив с непустым — хороший минимальный пример такой ошибки.

Итоговый проект: трекер привычек

Соберите небольшое приложение из уже изученных частей. Чистая модель хранит привычки и историю отметок; DOM-слой отображает данные; события преобразуются в действия; адаптер хранения сериализует согласованный формат. Расчёт серии занятий должен принимать данные и дату явно, чтобы его можно было проверить без реального текущего времени.

Начните с добавления и удаления привычки, затем реализуйте отметку дня и восстановление состояния. Проверьте пустую историю, несколько последовательных дней и разрыв серии. Не храните единственную истину только в тексте DOM: восстановление и тестирование будут сложнее. После работы объясните направление данных от события через модель к отображению и сохранению. Именно это соединяет отдельные темы курса в самостоятельную программу.

Загружаем редактор…
Ты дошёл до конца разделаЗакрепить знания в проекте

Музыка: Reverie — Scott Buckley · CC BY 4.0