Node.js Test Runner: Руководство для начинающих¶
Исторически сложилось так, что в Node.js отсутствовал интегрированный прогонщик тестов, что вынуждало разработчиков полагаться на сторонние фреймворки, такие как Jest или Mocha.
Ситуация изменилась, когда Джеймс М. Снелл предложил на GitHub включить в состав Node.js программу для запуска тестов. Предложение со временем развивалось и в итоге было включено в ядро Node.js.
В результате Node версии 18 и выше включает в себя встроенную программу запуска тестов, которая устраняет необходимость во внешних зависимостях для тестирования.
В этой статье вы познакомитесь с возможностями нового раннера тестирования и рассмотрите несколько примеров.
Предварительные условия¶
Прежде чем приступить к этому руководству, убедитесь, что у вас установлена последняя версия Node.js, предпочтительно последняя LTS.
Шаг 1 - Настройка директории¶
В этом разделе вы создадите директорию проекта для кода Node.js, который вы будете тестировать на протяжении всего этого урока.
Для начала создайте каталог и перейдите в него с помощью следующей команды:
1 |
|
Затем инициализируйте каталог как проект npm:
1 |
|
Эта команда создает файл package.json
, который содержит важные метаданные для вашего проекта.
После этого выполните приведенную ниже команду, чтобы включить ES-модули в вашем проекте:
1 |
|
Это добавит ключ type
в ваш файл package.json
и установит его значение на module
:
package.json | |
---|---|
1 2 3 4 |
|
Теперь вы готовы создать программу на Node.js, которую будете тестировать. Вот программа в полном объеме:
formatter.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
index.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
Приведенный выше код преобразует размеры файлов из байтов в более понятный для человека формат, например, КБ, МБ и т. д. Сохраните код в соответствующих файлах в корне проекта и протестируйте его, предоставляя различные входные данные:
1 |
|
1 |
|
1 |
|
Вот ожидаемые результаты:
Output | |
---|---|
1 2 3 |
|
Теперь, когда вы поняли, что нужно тестировать, перейдем к следующему разделу, где вы создадите свой первый тест с помощью тестового раннера Node.js.
Шаг 2 - Написание первого теста¶
Юнит-тестирование необходимо для проверки правильности работы функций в различных сценариях. Это подтверждает функциональность, а также служит документацией для будущих разработчиков.
В предыдущем разделе мы обсудили, что делает демонстрационная программа и каковы ее ожидаемые результаты. Вместо того чтобы вручную проверять эти результаты, вы создадите модульные тесты, чтобы автоматизировать этот процесс.
Начните с создания каталога tests
в корневой папке вашего проекта:
1 |
|
Затем создайте файл с именем formatter.test.js
внутри вашего tests
со следующим содержимым:
1 |
|
tests/formatter.test.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Здесь импортируется функция formatFileSize()
, а также функции тестирования describe
и it
из node:test
, и assert
из node:assert
.
Функция describe
оборачивает ваши тесты в группу, называемую тестовым набором, в котором вы можете определить несколько тестов. Тест, инкапсулированный в it
, проверяет, соответствует ли фактический вывод функции formatFileSize()
ожидаемому результату «1.00 GB». Такой автоматизированный подход гарантирует, что функция работает так, как задумано, без ручной проверки каждого вывода.
Шаг 3 - Запуск тестов¶
После того как вы сохранили файл с тестами, можно приступать к их выполнению. Чтобы запустить тест с помощью программы запуска тестов Node, используйте флаг --test
:
1 |
|
Эта команда побуждает Node.js искать файлы, соответствующие определенным шаблонам, начиная с текущего каталога и ниже:
- Файлы с расширениями
.js
,.cjs
или.mjs
, расположенные в каталогахtest
илиtests
. - Любой файл с именем
test
, независимо от расширения, например,test.js
. - Файлы, начинающиеся с
test-
, например,test-feature.cjs
. - Файлы, заканчивающиеся на
.test
,-test
или_test
, например,example.test.js
,example-test.cjs
,example_test.mjs
.
По умолчанию каждый совпадающий файл выполняется в отдельном дочернем процессе. Если дочерний процесс завершается с кодом выхода 0
, тест считается пройденным, в противном случае - неудачным.
После выполнения тестов вывод будет выглядеть следующим образом:
Output | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Вывод показывает, что тестовый набор с одним случаем для функции formatFileSize()
выполнился успешно, проверив, что она возвращает «1.00 GB» для 1073741824 байт без сбоев, отказов или пропусков. На выполнение теста ушло около 112,37 миллисекунды.
В программе тестирования используется формат TAP output format, который уже широко распространен в экосистеме.
Watch Mode¶
Node.js предлагает удобный режим watch mode, который автоматически отслеживает изменения в ваших тестовых файлах и зависимостях. Эта функция автоматически перезапускает тесты при обнаружении изменений, гарантируя, что ваш код продолжает соответствовать ожидаемым результатам.
Чтобы включить и использовать режим watch, вы можете воспользоваться следующей командой:
1 |
|
Output | |
---|---|
1 2 3 |
|
Шаг 4 - Тестирование с помощью синтаксиса test
¶
Вы уже изучили синтаксис describe
и it
для структурирования тестов, которые широко используются. Однако Node.js также поддерживает синтаксис test
, если вы предпочитаете другой стиль.
Вот как можно адаптировать предыдущий пример для использования синтаксиса test
:
tests/formatter.test.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
При запуске этот тест выдает результат, аналогичный тому, который вы получили в предыдущем разделе:
Output | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Очень важно использовать этот синтаксис аккуратно, следя за тем, чтобы подтесты были построены с помощью метода t.test()
. Использование функции верхнего уровня test()
для подтестов приведет к ошибке:
tests/formatter.test.js | |
---|---|
1 2 3 4 5 6 7 8 9 |
|
Output | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Синтаксис describe/it
менее подвержен подобным ошибкам, поэтому мы будем продолжать отдавать предпочтение этому синтаксису в этой статье для последовательности и ясности.
Шаг 5 - Фильтрация и ограничение тестов¶
При управлении набором тестов часто возникают ситуации, когда необходимо отфильтровать или ограничить выполнение определенных тестов. Это позволяет сфокусировать тестирование на определенных файлах, сценариях или условиях, не запуская каждый раз весь набор.
Фильтрация тестов по имени¶
Программа запуска тестов Node поддерживает выбор тестов с помощью аргумента --test-name-pattern
. Вот пример:
tests/formatter.test.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
Обратите внимание, что в некоторые тесты был добавлен тег @large
. Этот тег можно назвать как угодно. Чтобы запустить только тесты, помеченные @large
, используйте:
1 |
|
Результат выполнения этой команды будет выглядеть следующим образом:
Output | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Вы также можете запустить конкретный тест, используя уникальную часть имени теста, например, так:
1 |
|
Output | |
---|---|
1 2 3 4 5 6 7 8 |
|
Пропуск тестов с помощью skip
¶
Программа запуска тестов Node.js также предлагает метод skip()
для пропуска определенных тестов. Его можно использовать как describe.skip()
для пропуска всего набора, или как it.skip()
для пропуска подтеста. Вы можете написать его как:
tests/formatter.test.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
Или:
tests/formatter.test.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
При выполнении вывод покажет, что один тест был пропущен, а остальные выполнены:
Output | |
---|---|
1 2 3 4 5 6 7 8 |
|
Шаг 6 - Реализация mocks¶
В раннере тестирования Node.js есть встроенные возможности mocking, которые идеально подходят для тестирования внешних API, стороннего кода или методов. Это гарантирует, что ваши модульные тесты будут стабильными и не будут зависеть от внешних факторов, таких как сетевое подключение или изменения в файловой системе.
Например, метод readFile()
, который считывает данные с локального диска, может быть сымитирован, чтобы избежать фактического считывания данных с диска во время тестирования:
tests/index.test.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
В этом примере используется mock.method()
для замены реальной функции fs.promises.readFile()
на макетную функцию, которая возвращает Hello World
.
Тест проверяет, что макетная функция ведет себя так, как ожидается, гарантируя, что она вызывается правильно и выдает правильный результат. После выполнения теста используется mock.reset()
для очистки всех глобально отслеживаемых данных макета, что позволяет сохранить изоляцию теста.
После выполнения эта тестовая установка выдаст следующий результат:
Output | |
---|---|
1 2 3 4 5 |
|
Имитация таймеров¶
В Node.js v20.4.0 был представлен API MockTimers
, позволяющий моделировать функции, связанные со временем, такие как setTimeout()
и setInterval()
.
Вот как использовать эту возможность с помощью mock.timers.enable()
:
tests/index.test.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
В этом примере функция setTimeout
переопределена, чтобы протестировать ее поведение, не дожидаясь истечения реального времени. Тест инициирует таймаут для выполнения функции через 20 миллисекунд, а затем переводит часы на два шага по 10 миллисекунд.
Несмотря на то, что общее время симуляции совпадает с длительностью таймаута, функция считается вызванной только один раз благодаря настройке имитации. Этот метод обеспечивает точный способ тестирования кода, зависящего от времени, без реальной задержки.
При запуске вывод выглядит следующим образом:
Output | |
---|---|
1 2 3 4 5 6 7 |
|
Вы также можете использовать API MockTimers
для имитации и контроля поведения объекта Date
, что невероятно полезно для тестирования функций, зависящих от времени, таких как Date.now()
.
В следующем примере показано, как имитировать объект Date
с помощью этого API:
tests/index.test.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
В этом примере начальное время объекта Date
составляет 200 миллисекунд. Тест проверяет, что Date.now()
изначально совпадает с этой макетной установкой. Затем он увеличивает время на 200 миллисекунд с помощью context.mock.timers.tick(200)
и подтверждает, что Date.now()
обновляется соответственно до 400 миллисекунд.
При запуске вывод выглядит примерно так:
Output | |
---|---|
1 2 3 4 5 6 7 |
|
Шаг 7 - Использование тестовых крючков для задач установки и завершения работы¶
Еще одна полезная функция, предлагаемая программой запуска тестов, - это хуки, которые обычно используются для задач установки и завершения. Установка включает в себя настройку окружения для теста, в то время как удаление связано с очисткой или сбросом всего, что было установлено на этапе установки.
Предоставляются следующие хуки:
before
: Этот хук запускается один раз перед выполнением любого теста. Он часто используется для настройки окружения, например, для установки соединения с базой данных или загрузки фикстур.beforeEach()
: Этот хук запускается перед каждым тестом в наборе. Он помогает настроить необходимые вещи для каждого теста, например, инициализировать объекты или тестовые данные.after()
: Этот хук запускается после завершения всего набора тестов. Он может использоваться для очистки ресурсов, например, для закрытия соединений с базой данных.afterEach()
: Этот хук выполняет код после каждого тестового случая.
В предыдущем разделе, посвященном имитации, вы видели пример имитации метода с помощью mock.method
и сброса имитации. Аналогичные приемы можно использовать и с хуками, как показано ниже:
tests/index.test.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
Здесь метод fs.readFile()
подражается перед каждым тестом в хуке beforeEach()
и сбрасывается в afterEach()
, чтобы последующие тесты начинались с чистого состояния. Такой подход позволяет избежать проблем, вызванных общим состоянием между тестами.
После выполнения тестов вы увидите примерно такие результаты:
Output | |
---|---|
1 2 3 4 5 |
|
Шаг 9 - Измерение покрытия кода¶
Программа для запуска тестов Node.js включает экспериментальную функцию для отслеживания покрытия кода, которая включается с помощью флага --experimental-test-coverage
. Это позволяет получить подробную информацию о том, какая часть вашей программы выполняется вашими тестами, что является ценной метрикой для понимания эффективности тестов.
Чтобы собрать информацию о покрытии кода, используйте следующую команду:
1 |
|
Соответствующая часть отчета приведена ниже:
Output | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Отчет о покрытии показывает процент покрытия кода проекта: файл formatter.js
имеет 83,33 % покрытия строк, 66,67 % покрытия ветвей и 100 % покрытия функций, при этом строки 3-4 не покрыты.
Кроме того, Node.js позволяет выборочно отключать анализ покрытия кода для определенных участков кода. Это полезно для тех частей кода, которые специфичны для конкретной платформы или сложны для тестирования. Вы можете отключить, а затем снова включить анализ покрытия следующим образом:
1 2 3 4 5 |
|
При использовании этих комментариев инструмент покрытия кода будет игнорировать прилагаемый блок кода во время анализа. Эта функция помогает поддерживать точность показателей покрытия за счет исключения участков, которые не способствуют функциональности приложения или нецелесообразно тестировать.
Шаг 10 - Генерация тестовых отчетов¶
Прогонщик тестов Node.js поддерживает генерацию подробных отчетов о тестировании, которые неоценимы для анализа, выявления тенденций и ведения документации по результатам тестирования. Вы можете указать формат этих отчетов с помощью флага --test-reporter
.
Поддерживаемые форматы отчетов включают:
- TAP (Test Anything Protocol): Этот формат универсален и может использоваться с другими инструментами, которые анализируют вывод TAP.
- Spec: Предоставляет описательный вывод, который легко читать непосредственно в терминале.
- Dot: Отображает минималистичную матрицу, где каждая точка представляет тест.
- JUnit: Генерирует XML-отчеты, которые можно интегрировать с инструментами непрерывной интеграции и другими системами, использующими JUnit-совместимые форматы.
Чтобы сгенерировать отчет в формате TAP, выполните следующие действия:
1 |
|
Output | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Для отчета в формате Spec
используйте:
1 |
|
Вы также можете направить вывод этих отчетов в файл, что особенно полезно для архивации или интеграции с другими инструментами. Например, чтобы сохранить отчет TAP в файл:
1 |
|
Эта команда генерирует файл report.txt
, содержащий отчет в формате TAP.
Шаг 11 - Написание базового теста для сервера¶
В этом разделе мы покажем, как написать базовый тест для сервера Node.js. Здесь мы будем использовать Fastify, но этот подход может быть адаптирован и для других подобных веб-фреймворков.
Сначала добавьте Fastify в свой проект с помощью npm:
1 |
|
Далее настройте базовый сервер Fastify в корне проекта:
app.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Приведенный выше код определяет один маршрут, который возвращает объект JSON при посещении. Теперь давайте напишем тесты, чтобы убедиться, что наше приложение Fastify ведет себя так, как ожидается:
tests/app.test.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
В нашей тестовой схеме хук before
инициализирует приложение Fastify, чтобы убедиться, что оно готово к тестированию, а хук after
закрывает сервер, чтобы освободить ресурсы.
Блок it
оценивает ответ приложения, проверяя наличие кода состояния 200, правильного типа содержимого и ожидаемого вывода JSON, тем самым гарантируя, что каждый тест проводится в оптимальных условиях для получения точных результатов.
Выполните тест сервера с помощью программы запуска тестов Node:
1 |
|
Output | |
---|---|
1 2 3 4 5 |
|
Эта настройка тестирования обеспечивает надежный способ убедиться, что ваши приложения Fastify работают правильно в контролируемой среде с помощью тестового раннера Node.
Заключительные мысли¶
В этой статье представлено подробное руководство по использованию раннера тестирования Node.js для создания и выполнения тестов. В ней освещены такие функции, как мокинг, анализ покрытия кода и генерация отчетов, которые необходимы для оценки эффективности тестов.
Для получения дополнительной информации посетите страницу документации. Если вам интересно узнать, как Node.js test runner сравнивается с другими фреймворками для тестирования, ознакомьтесь с нашим руководством по сравнению.
Источник — https://betterstack.com/community/guides/testing/nodejs-test-runner/