Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Модульные тесты JS: Автоматическое тестирование

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

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

Попробуем протестировать стек. Напомню, что стек представляет собой список элементов организованных по принципу LIFO. Данные кладутся в стек в одном порядке, а извлекаются в обратном. Сам стек, как правило, используется для реализации алгоритмов. Он часто используется в низкоуровневом коде: например, внутри языков программирования или в операционных системах.

import makeStack from '../src/stack.js';

const stack = makeStack();
stack.isEmpty(); // true
stack.push(1); // (1)
stack.push(2); // (1, 2)
stack.push(3); // (1, 2, 3)
stack.isEmpty(); // false
stack.pop(); // 3. В стеке (1, 2)
stack.pop(); // 2. В стеке (1)
stack.pop(); // 1. В стеке пусто
stack.isEmpty(); // true

Сначала решим организационные вопросы. Если предположить, что реализация стека лежит в файле src/stack.js, то его тест мы положим в файл __tests__/stack.test.js.

Тестируем основную функциональность

Теперь напишем первый тест. Первый тест всегда должен проверять позитивный сценарий — тот, в котором задействована основная функциональность тестируемого компонента:

import makeStack from '../src/stack.js';

test("stack's main flow", () => {
  const stack = makeStack();
  // Добавляем два элемента в стек и затем извлекаем их
  stack.push('one');
  stack.push('two');
  expect(stack.pop()).toEqual('two');
  expect(stack.pop()).toEqual('one');
});

Этот тест проверяет, что правильно работают два основных метода без учёта пограничных случаев. Для этого внутри теста выполняются два матчера, которые по очереди проверяют извлекаемые значения из стека.

В интернете можно встретить мнение, что несколько проверок в рамках одного теста это неправильно. Что тесты нужно детализировать максимально подробно и создавать новый тест на каждую проверку.

test("stack's main flow", () => {
  const stack = makeStack();
  stack.push('one');
  stack.push('two');
  expect(stack.pop()).toEqual('two');
});

test("stack's main flow", () => {
  const stack = makeStack();
  stack.push('one');
  stack.push('two');
  stack.pop();
  expect(stack.pop()).toEqual('one');
});

Такой подход нередко приводит к серьёзному раздуванию кода и дублированию. А выгода не очевидна. Что по-настоящему надо выделять в отдельный тест, так это другой сценарий, которому нужны другие данные и который выполняет другую последовательность действий.

Тестируем дополнительную функциональность

Следующим тестом будет тест на дополнительные функции стека. К таким у нас относится функция isEmpty(), которая проверяет, пустой ли стек:

test('isEmpty', () => {
  const stack = makeStack();
  expect(stack.isEmpty()).toBe(true);
  stack.push('two');
  expect(stack.isEmpty()).toBe(false);
  stack.pop();
  expect(stack.isEmpty()).toBe(true);
});

В этом тесте проверяются сразу три ситуации:

  • начальное состояние стека
  • состояние стека после добавления элементов
  • состояние стека после извлечения всех элементов

В принципе, этого достаточно. Хотя в теории возможны ситуации, при которых isEmpty() всё равно сломается. Нужно ли пытаться найти все варианты? Не нужно. Тесты не даются бесплатно, каждая написанная строчка кода в проекте — потенциальное место для изменения в случае правок. Если есть сомнения, нужно ли писать проверку или нет, то лучше не пишите. Так вы поймёте тот минимум, который стоит писать, и после которого тесты писать не эффективно. Редкие ситуации требуют покрытия тестами только тогда, когда они критичны для работоспособности.

Пограничные случаи

Ну, и последнее, что можно протестировать — поведение функции pop(), когда в стеке нет ни одного элемента. По задумке, стек выбрасывает исключение, если из него попытались взять элемент, когда тот был пустой. То есть эта ситуация рассматривается как ошибочная, программист всегда должен убеждаться в том что стек не пустой.

test('pop in empty stack', () => {
  const stack = makeStack();
  // Вызов метода pop обёрнут в функцию
  // иначе матчер не сможет перехватить исключение
  expect(() => stack.pop()).toThrow();
});

https://repl.it/@hexlet/js-testing-unit-tests-stack#tests/stack.test.js

Но не всегда пограничные случаи так легко увидеть. Маловероятно, что любой программист сможет сразу написать все нужные тесты. Если в коде возникла ошибка, для которой не было теста, то сначала напишите тест, который воспроизводит эту ошибку, и затем уже чините её. Только так можно поддерживать достаточный уровень надёжности, не превращая разработку в непрерывную починку багов.

Домашнее задание

Загрузите домашнее задание с помощью команды:

hexlet program download frontend-testing-react unit-testing

Дополнительные материалы

  1. Чек-лист хороших инженерных практик в компаниях

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Для полного доступа к курсу нужна профессиональная подписка

Профессиональная подписка откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
900
упражнения
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 120 курсов, 2000+ часов теории
  • 900 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы Фронтенд-разработчик
Профессия
Разработка фронтенд-компонентов веб-приложений
22 сентября 8 месяцев
Иконка программы Node.js-разработчик
Профессия
Разработка бэкенд-компонентов веб-приложений
22 сентября 8 месяцев

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»