🧪 Успешное тестирование: основы и передовые приемы Jest. Часть 1
Знаком с ситуацией, когда после деплоя все падает, а ты не знаешь почему? В этой статье я показываю, как из обычного разработчика стать профи в тестировании с Jest. Разобрал все от и до: настройка проекта, базовые тесты, моки и даже асинхронщина. Плюс весь код доступен на GitHub – бери и используй! Если тестирование вызывает у тебя дрожь – этот гайд изменит твою жизнь.
Привет, друзья! Я Кирилл Мыльников, frontend-разработчик в ГК Юзтех. Сегодня хочу поделиться своей экспертизой в тестировании с использованием фреймворка Jest. Полностью настроенный проект будет доступен на GitHub, где вы сможете ознакомиться с ним и клонировать его. Мы рассмотрим широкий спектр тем – от базовых до продвинутых приёмов тестирования. Статья будет полезна как для тех, кто хочет освежить свои знания, так и для новичков, только начинающих погружаться в мир тестирования. Также отмечу, что статья состоит из нескольких частей.
В серии статей разберем следующие темы:
Jest основы:
- структура тестов;
- виды проверок;
- настройка и запуск.
Особенности Jest:
- моки и для чего они нужны;
- тестирование ошибок и покрытие кода.
Прежде чем приступим к практике, давайте вспомним, какие виды тестирования существуют.
Виды тестирования
Unit-тесты
Unit-тесты — это форма тестирования, которая направлена на проверку правильности работы отдельных небольших компонентов кода, таких как функции, классы, модули, компоненты и другие. Они помогают выявлять ошибки и оперативно их устранять при внесении изменений или добавлении нового функционала.
Интеграционное тестирование
Интеграционное тестирование — это тестирование на работоспособность двух или более модулей системы. Они решают проблему после покрытия кода unit-тестов, проверяют, как ведут себя связные модули.
End to End
End to End — это метод тестирования, который проверяет полностью функциональности ПО от начала до конца, включает в себя проверку всех уровней системы, начиная от пользовательского интерфейса и заканчивая базой данных и серверной логикой. В основном, это робот, имитирующий пользователя.
UI тестирование
UI тестирование — это метод тестирования, направленный на корректность работы и удобства пользовательского интерфейса, например: внешний вид интерфейса, отзывчивость, элементы управления и так далее.
Основная стратегия тестирования представлена в виде пирамиды, которая показывает, что начинать следует с юнит-тестов, затем переходить к интеграционным тестам, чтобы сочетать их, и, наконец, проводить end-to-end тесты. Идея пирамиды заключается в том, что чем выше находится тестовый уровень в пирамиде, тем тесты дороже. Ну и этот подход обычно подходит новым проектам, где сразу пишем тесты.
Когда проект уже существует, стратегия тестирования может быть адаптирована. В таких случаях часто начинают с проведения end-to-end тестов для проверки работы приложения в целом. Затем переходят к интеграционным тестам, чтобы убедиться в правильном взаимодействии компонентов. И только после этого фокусируются на unit-тестах, чтобы покрыть отдельные части логики приложения. Такой подход помогает обеспечить надёжное тестирование всего приложения в целом перед более детальной проверкой отдельных компонентов.
Итак, мы с вами разобрали, какие виды тестирования существуют. Пора переходить к практике, а именно, к Jest и его основам.
Jest
Jest — это фреймворк для тестирования JavaScript, который обладает удобным синтаксисом для написания и запуска тестов. Он хорошо документирован, легко настраивается и может быть легко расширен по мере необходимости.
Основные возможности:
- Детальное описание причин падения тестов;
- Наличие полноценного набора инструментов из коробки;
- Быстрый и надёжный запуск тестов в параллельных потоках;
- Работа с проектами, использующими Babel, Rect, Vue, Angular, TypeScript, Node;
- Можно писать как и unit-тесты, так и интеграционные;
- Инструменты для имитации.
Вообще, у нас будет два отдельных проекта-примера, один естественно с React (реализация будет в другой статье), а другой просто настроим с нуля.
Создаём папку, где у вас будет проект, и инициализируем проект, в моём случае с помощью команды:
Должна появиться такая структура:
После чего нужно будет установить сам пакет Jest с помощью команды:
Как говорится в документации Jest, для расширенных настроек понадобится установить ещё ряд пакетов:
После установки всех необходимых пакетов создаём файл babel.config.js в корневой директории проекта и указываем необходимые конфигурационные параметры. Если мы хотим использовать TypeScript, мы должны установить соответствующий пакет и добавить соответствующую зависимость в наш файл конфигурации:
Не забывайте про важную деталь: чтобы Jest проверял типы по мере их выполнения, нужно установить ещё один пакет ts-jest и для предоставления глобальных типов @types/jest
Давайте пока переключимся на конфигурацию Jest. В корне проекта создаём jest.config.ts и указываем следующую настройку:
Опция verbose: true обеспечивает более подробный вывод информации при запуске тестов в случае возникновения проблем.
Используем preset: “ts-jest” для преобразования TypeScript в JavaScript перед запуском тестов.
Также импортируем тип Config для получения подсказок о других доступных настройках в целом.
Можно теперь проверить, запускаются ли тесты вообще. В package-json указываем команду:
Создаём файл utils, в котором содержится функция, выполняющая деление двух чисел. Нам необходимо протестировать эту функцию, поэтому создаём файл utils.test.ts и пишем первый тест.
И запускаем с помощью команды yarn start. Если всё настроили правильно, то должно быть так:
В одном файле можно создавать произвольное количество тестов, используя директивы test или it. Хорошей практикой является начинать названия тестов с ключевого слова "should", чтобы чётко определить ожидаемое поведение. Это способствует пониманию того, что тестируется, и повышает читаемость кода. Кроме того, использование describe() для группировки тестов помогает упорядочить и структурировать код, делая его более ясным и лёгким для поддержки. Важно быть особенно внимательным к именованию тестов, чтобы обеспечить их эффективную организацию и понимание.
Пример:
Результат:
Давайте разберём, что такое expect. Это глобальная функция, в которую мы передаём определённый результат и которая возвращает нам объект с разными вариантами проверок. Основные проверки, которые используются:
toBe() — работает как тройное сравнение, идёт проверка на true/false, хорошо подходит для примитивов:
toEqual() — работает с объектами, сравнивает и возвращает true, если равны объекты, в противном случае false.
toHaveLength() — проверяет длину массива.
toContain() — проверяет, есть ли конкретный элемент в массиве.
toBeUndefined() и toBeNull() — проверяют результат на null/undefined.
Думаю, суть вы поняли, проверок достаточно много. О них можно почитать в официальной документации Jest.
Параметризованные тесты
Параметризованные тесты позволяют эффективно повторно использовать один и тот же тестовый сценарий при различных условиях на каждом запуске. Это оптимизирует управление тестами, уменьшает дублирование кода и делает процесс тестирования более гибким и обслуживаемым.
Возможности test и it включают методы, такие как skip, который позволяет игнорировать определенный тест, и only, который позволяет выполнять только выбранные тесты, оставляя остальные в игноре. Эти функции облегчают управление тестами.
only:
skip:
Этих методов у нас достаточно много. Подробнее можете посмотреть в официальной документации Jest.
Особенности Jest
Mocks — это фейковые данные, которые нужны для тестового кейса.
Spies — отслеживание работы методов.
Функции-фейки — нужны для тестирования количества вызовов колбэков.
Что такое моки (mocks)?
Давайте рассмотрим пример тестирования с мок-данными. У нас есть функция, которая фильтрует массив:
Пример:
- В первом случае мы проверяем, что функция не была вызвана;
- Во втором случае мы проверяем, что функция была вызвана, получила массив и вернула количество вызовов;
- В третьем случае мы используем предоставленные мок-данные для возвращения отфильтрованного массива.
Те самые мок-данные можно посмотреть тут, где result_mocks_data — результат после фильтрации, mocks_data — просто мок-данные, которые получает функция.
Результат:
Следующее, что мы с вами разберём, это Spies
Spies
Spies используют для отслеживания вызовов конкретных функций с целью убедиться, что они были вызваны или вызваны с определенными параметрами. Рассмотрим следующий случай: мы хотим отследить console.log, который используется в нашем цикле. Для этого мы можем использовать метод spyOn, который принимает два аргумента. Первый аргумент — это объект, за которым мы хотим следить (в данном случае это console), а второй аргумент — это метод объекта, за которым мы хотим следить (например, log).
Пример:
Результат:
Мы видим конкретные элементы массива.
Тестирование асинхронного кода
Давайте создадим асинхронную функцию, которая будет создавать нам что-то:
И сразу напишем тесты к данной функции:
Пример:
Далее в тесте используем метод mockReturnValueOnce, чтобы задать поведение моковой функции fetch. Мы возвращаем объект с флагом ok: true и методом json, который возвращает объект задачи из тестовых данных.
Мок-данные, которые мы ожидаем, в ответе.
Пример:
Тестирование ошибок
Теперь нужно проверить негативные кейсы, когда приходит ошибка. Оставляем ту же функцию, но теперь пишем вот такой тест-кейс:
В функции createTodoAsync ожидаем, что если ошибка, то выводим текст «Ошибка создания задачи».
Пример:
В данном примере изменили мок-данные на значение ok: false и ожидаем появления ошибки с конкретным текстом, который определён в функции.
Покрытие кода тестами
Покрытие кода тестами — это процент кода, который выполняется во время запуска тестов. Чем выше покрытие тестами, тем больше уверенности, что код работает правильно. Статистика покрытия тестами помогает определить, какие части кода нуждаются в дополнительных тестах. Она позволяет улучшить качество кода и обнаружить потенциальные проблемы.
Чтобы начать собирать статистику тестов по проекту, нам нужно прописать дополнительные настройки в файле jest.config.ts
Прописываем такое свойство:
Этим свойством мы разрешаем сбор информации, по умолчанию — false.
Дальше прописываем:
Указываем в виде массива пути, которые мы хотим протестировать или не хотим. К примеру, всё, что начинается со знака "!", мы отмечаем как папки или файлы, которые будут проигнорированы.
Если всё сделали правильно, запускаем тесты и проверяем статистику.
Результат:
Теперь мы видим, что протестировано, а что нет. Написано, где именно, например, в файле createTodo забыли протестировать функцию.
Давайте напишем для неё тесты и опять запустим.
Тесты к функции.
Запускаем снова тесты.
Результат:
Также обратите внимание, что в корне проекта у нас создалась папка coverage, там находится вся информация о тестах.
Больше всего нас интересует index.html который нам нужно открыть.
Мы увидим таблицу, которая у нас была в console, но тут можем провалиться в файл и посмотреть, что конкретно у нас не протестировано.
Пример:
Также в конфиге можем указать пороговые значения тестов для нашего проекта:
Запускаем и видим, что мы не прошли порог по тестам.
Результат:
Итак, мы с вами создали проект «с нуля» и прошли базовые темы. Важно, что мы научились настраивать конфигурации для различных задач, работать с jest. Напоминаю, что это лишь первая часть. Впереди нас ждут более сложные и захватывающие задачи.
Материалы
Jest — https://jestjs.io/
GitHub — https://github.com/kirill0202/Jest