Модульные тесты на JavaScript и Jest
В этой статье представлено краткое введение в Jest и концепции модульного тестирования. Мы научимся устанавливать Jest, писать наборы тестов с тест-кейсами и фикстурами, а также запускать тесты как с отчетами о покрытии, так и без них.
Что такое Jest?
Jest — это среда тестирования JavaScript, разработанная, чтобы максимально упростить тестирование. Он предоставляет все необходимые инструменты для запуска тестов, создания утверждений, макетирования реализаций и многого другого в одном пакете.
До Jest экосистема JavaScript опиралась на несколько различных инструментов и платформ, которые давали разработчикам возможность писать и запускать тесты. Настройка этих инструментов редко была простой и легкой. Jest стремится исправить это, используя разумные конфигурации по умолчанию, которые работают «из коробки», практически не требуя дополнительной настройки в большинстве случаев.
Jest также поддерживает TypeScript через Babel.
Как установить Jest?
Установите jest
пакет (и дополнительные типы) в новый или существующий файл проекта, package.json
используя выбранный вами менеджер пакетов:
# For NPM users npm install --save-dev jest @types/jest # Yarn users yarn add --dev jest @types/jest
Вот и все! Теперь мы готовы запускать тесты с помощью Jest.
Рекомендуется устанавливать Jest и любые другие инструменты тестирования в качестве зависимостей разработки. Это ускоряет установку в средах, где установлены только зависимости, необходимые для сборки и запуска проекта.
Как запускать тесты с помощью Jest?
Чтобы запустить тесты с помощью Jest, вызовите jest
команду в корне папки проекта.
Мы обновим проект package.json
тестовым скриптом, который вызывает jest
для нас команду:
{ // ... package.json contents "scripts": { // ... existing scripts "test": "jest" } }
Теперь мы можем запустить только что созданный test
скрипт:
# NPM users npm run test # Yarn users yarn run test
Если все настроено правильно, Jest предоставит нам результаты всех тестов, которые он нашел и выполнил.
Jest завершает работу с кодом состояния 1, если тестовый пример не пройден. В этом случае ожидается появление npm ERR!
ошибок в консоли.
Как создать тест с помощью Jest?
Чтобы создать тест для использования с Jest, мы создаем файл *.spec.js
или *.test.js
, который будет содержать наши тестовые примеры.
Jest настроен на поиск файлов.js
,.jsx
,.ts
, и.tsx
внутри папок, а также любых файлов с суффиксом__tests__
или.spec
(включая файлы с именамиtest
илиspec
).
Поскольку isInteger.js
это имя тестируемого модуля, мы будем записывать наши тесты в файл, isInteger.spec.js
созданный в той же папке, что и модуль:
// isInteger.spec.js test("Sanity check", () => { expect(true).toBe(true); });
Независимо от того, решите ли вы писать тесты в отдельной папке или рядом с модулями, не существует правильного или неправильного способа структурировать тесты внутри проекта. Jest достаточно гибок, чтобы работать с большинством архитектур проектов без настройки.
Описание теста — «Проверка работоспособности». Проверки работоспособности — это базовые тесты, гарантирующие, что система ведет себя рационально. Тест подтвердит, что мы ожидаем, что значение true
будет true
.
Запустите тест, и если он пройден, все настроено правильно.
Поздравляем! Мы только что написали наш первый тест!
Как написать тестовый пример в Jest?
Чтобы написать тестовый пример, мы сначала определяем результаты, которые мы должны проверить, чтобы убедиться, что система работает правильно.
Модуль isInteger.js
— это функция, которая принимает один параметр и возвращает значение true
, является ли параметр целочисленным значением или false
нет. Мы можем создать два тестовых примера из этого определения:
isInteger()
проходит для целочисленного значения;isInteger()
не работает для нецелого значения.
Чтобы создать тестовый пример в Jest, мы используем test()
функцию . В качестве первых двух аргументов он принимает строку имени теста и функцию-обработчик.
Функциюtest()
также можно вызывать под псевдонимом -it()
. Выбирайте тот или иной вариант в зависимости от читабельности или личных предпочтений.
Тесты основаны на утверждениях. Утверждения состоят из ожиданий и соответствий . Самое простое и распространенное утверждение предполагает, что проверяемое значение соответствует определенному значению.
Ожидание создается с помощью expect()
функции . Он возвращает объект методов сопоставления, с помощью которого мы утверждаем что-то ожидаемое относительно проверяемого значения.
Метод matcher toBe()
проверяет, соответствует ли ожидание заданному значению.
В наших тестах мы можем ожидать isInteger()
целочисленное true
значение 1 и false
нецелое значение 1,23.
// isInteger.spec.js const isInteger = require("./isInteger"); test("isInteger passes for integer value", () => { expect(isInteger(1)).toBe(true); }); test("isInteger fails for non-integer value", () => { expect(isInteger(1.23)).toBe(false); });
Запуск Jest теперь должен предоставить нам отчет о том, какие тесты пройдены, а какие не пройдены.
Как использовать фикстуры в Jest?
Чтобы использовать фикстуры в Jest, мы можем использовать test.each()
функцию . Он выполняет тест для каждого прибора в массиве приборов.
Фикстуры — это данные, представляющие условия, такие как аргументы функции и возвращаемые значения, при которых выполняется модульный тест. Использование фикстур — это быстрый и простой способ убедиться, что поведение модуля соответствует ожиданиям в различных условиях, без необходимости писать несколько тестов.
В Jest фикстура может представлять собой одно значение или массив значений. Фикстура доступна в функции обработчика теста через параметры. Значение или значения фикстуры можно ввести в описание посредством форматирования printf.
// isInteger.spec.js const isInteger = require("./isInteger"); const integerNumbers = [-1, 0, 1]; test.each(integerNumbers)( "isInteger passes for integer value %j", (fixture) => expect(isInteger(fixture)).toBe(true) ); // ... or... const integerNumbers = [ [-1, true], [-0, true], [1, true] ]; test.each(integerNumbers)( "isInteger passes for integer value %j with result %j", (fixture, result) => expect(isInteger(fixture)).toBe(result) );
Запуск Jest теперь должен предоставить нам отчет о том, какие тесты пройдены, а какие не пройдены, причем каждый тест соответствует фикстуре из нашего массива фикстур.
%j
— это спецификатор форматирования printf , который печатает значение в формате JSON. Это хороший выбор для приборов, содержащих значения разных типов.
Как сгруппировать тестовые случаи в Jest в набор тестов?
Чтобы сгруппировать тестовые случаи в Jest в набор тестов, мы можем использовать describe()
функцию. В качестве первых двух аргументов он принимает строку имени набора и функцию-обработчик.
Набор тестов — это набор тестовых примеров, сгруппированных вместе для целей выполнения.
Цель набора тестов — организовать тесты по общему поведению или функциональности. Если все тесты в наборе пройдены, мы можем предположить, что тестируемое поведение или функциональность соответствуют ожиданиям.
// isInteger.spec.js const isInteger = require("./isInteger"); describe("isInteger", () => { const integerNumbers = [-10, -1, 0, 1, 10]; test.each(integerNumbers)( "passes for integer value %j", (fixture) => expect(isInteger(fixture)).toBe(true) ); const floatNumbers = [-10.1, -1.1, 0.1, 1.1, 10.1]; test.each(floatNumbers)( "fails for non-integer value %j", (fixture) => expect(isInteger(fixture)).toBe(false) ); });
Запуск Jest теперь должен предоставить нам отчет о том, какие тесты пройдены, а какие не пройдены, сгруппированные в описанные наборы тестов.
describe()
также можно вкладывать друг в друга для создания более сложных иерархий тестов.
Как запускать Jest каждый раз, когда файлы меняются?
Чтобы запускать Jest каждый раз при изменении файлов, мы можем использовать флаги--watch
и --watchAll
.
Флаг --watch
сообщит Jest следить за изменениями в файлах, отслеживаемых Git. Jest запустит только те тесты, на которые повлияли измененные файлы. Чтобы это работало, проект также должен быть репозиторием Git.
Флаг --watchAll
сообщит Jest следить за изменениями во всех файлах. Всякий раз, когда файл изменяется, Jest запускает все тесты.
Оба режима --watch
и --watchAll
поддерживают дополнительную фильтрацию тестов во время их выполнения. Это позволяет запускать только тесты, соответствующие имени файла, или запускать только неудачные тесты.
# Runs tests on changed files only and re-runs for any new change # Note: the project must also be a git repository jest --watch # Runs tests on all files and re-runs for any new change jest --watchAll
Как получить отчет о покрытии тестами с помощью Jest?
Чтобы получить отчет о тестовом покрытии с помощью Jest, мы можем использовать --coverage
флаг.
Покрытие тестами — это метрика тестирования программного обеспечения, которая описывает, сколько строк исходного кода (операторов) тестируемого модуля выполняется (покрывается) тестами. 100%-ное покрытие теста для модуля означает, что каждая строка кода в модуле была вызвана тестом.
Мы всегда должны стремиться к высокому покрытию тестами — в идеале 100 % — но также имейте в виду, что полное покрытие не означает, что мы протестировали все случаи, а только строки кода.
# Runs tests and prints a test coverage afterwards jest --coverage
Мы можем комбинировать разные флаги, чтобы получить больше возможностей от Jest. Например, чтобы просмотреть все файлы и получить отчет о покрытии, мы можем запустить jest --watchAll --coverage
.
На этом у нас все готово! Теперь мы можем писать тесты и запускать их при каждом изменении файла, а также просматривать отчеты о покрытии тестами для пройденных и непокрытых строк кода.
Пример кода модульного теста Jest
Чтобы установить Jest:
# For NPM users npm install --save-dev jest @types/jest # Yarn users yarn add --dev jest @types/jest
Устройство, которое будет тестироваться в isInteger.js
:
// isInteger.js module.exports = (value) => !isNaN(parseInt(value, 10));
Модульный тест в isInteger.spec.js
:
// isInteger.spec.js const isInteger = require("./isInteger"); describe("isInteger", () => { const integerNumbers = [-10, -1, 0, 1, 10]; test.each(integerNumbers)( "passes for integer value %j", (fixture) => expect(isInteger(fixture)).toBe(true) ); const floatNumbers = [-10.1, -1.1, 0.1, 1.1, 10.1]; test.each(floatNumbers)( "fails for non-integer value %j", (fixture) => expect(isInteger(fixture)).toBe(false) ); });
Тестовый скрипт в package.json
:
jest --watchAll --coverage