Стоит ли использовать jest в качестве тестовой библиотеки?
Узнайте, почему это нарушает работу оператора instanceof
jest, популярный фреймворк для тестирования, созданный Facebook с более чем 50 миллионами загрузок в месяц, вызывает много проблем у бэкэнд-разработчиков.
В этой статье я попытаюсь кратко описать раздражение и восторг от jest, а также то, как с этим бороться и что является ее причиной.
Как работает jest?
jest
— это платформа для тестирования JavaScript с множеством функций, в том числе isolated
, написанных на ее веб-сайте. Целевая производительность функции изоляции:
Тесты распараллеливаются путем запуска их в их собственных процессах для максимизации производительности.
Изоляция jest
обусловлена его архитектурой, в которой под капотом используется основной модуль node:vm
.
Модуль vm
позволяет jest
запускать каждый тестовый файл в песочнице с собственным временным контекстом. Контекст включает в себя все классы global
, такие как Array
, Error
, Date
и многие другие — например, функцию describe
и функцию тестирования it
для примера. (Здесь исходный код Jest, который делает свое дело)
Так как jest
перезаписывает некоторые из этих компонентов, чтобы предоставить вам модные функции, такие как макеты, фальшивые часы и быстрое выполнение теста, он должен установить все vm
данные global
, чтобы настроить контекст, в котором будет выполняться исходный код теста.
К сожалению, когда основные модули Node.js создают новый экземпляр класса global
, они не будут использовать контекст vm
, а возвращаются к собственной реализации.
Это означает, что оператор instanceof
не будет работать должным образом и будет генерировать ложные отрицательные значения!
Вы можете получить быстрый пример этой проблемы в следующем фрагменте файла test.js
:
const { parseArgs } = require('util')
test('Array is not an Array', async () => {
const { values } = parseArgs({
args: ['--bar', 'a', '--bar', 'b'],
options: {
bar: {
type: 'string',
multiple: true
}
}
})
expect(values.bar).toEqual(['a', 'b'])
expect(values.bar).toBeInstanceOf(Array) // ❌ it will fail
})
Запустив приведенный выше тест с помощью команды jest test.js
(с конфигурацией jest
по умолчанию), он завершится с ошибкой:
● Array is not an Array
expect(received).toBeInstanceOf(expected)
Expected constructor: Array
Received constructor: Array
27 | })
28 | expect(values.bar).toEqual(['a', 'b'])
> 29 | expect(values.bar).toBeInstanceOf(Array)
Это может показаться незначительной проблемой, нужно просто избегать использования оператора instanceof
, но это не так. Более серьезная проблема заключается в том, что оператор instanceof
может использоваться вашим деревом зависимостей для выполнения некоторых проверок, и эти условия не будут выполнены.
Например, Fastify удалил оператор instanceof
из своей кодовой базы, потому что он вызывал проблемы у тех разработчиков, которые полагаются на jest
в качестве основы для тестирования.
Как это исправить?
Вы не можете выйти из коробки. Существует открытый вопрос по Node.js репозиторий позволяет модулю node:vm
использовать контекст vm
, но он все еще открыт. Похоже, что Node.js основная команда заинтересована в устранении этой проблемы путем внедрения новой спецификации Shadow Realm, и я думаю, что мы добьемся некоторого прогресса в течение 2023 года.
Если вы не можете ждать, есть очень быстрое решение, используя пользовательскую среду тестирования jest-environment-node-single-context
.
Если вы установите этот модуль и запустите предыдущий код с помощью команды:
jest --testEnvironment jest-environment-node-single-context test.js
Теперь все будет работать как положено:
PASS ./test.js
✓ Array is not an Array (5 ms)
⚠️ Обратите внимание, что теперь тест работает, потому что функция изоляции jest
полностью отключена при запуске всех тестов в одном контексте.
Другим рабочим решением является использование нового jest
runner: jest-light-runner
. Это раскручивает Node.js рабочий поток для каждого тестового файла предоставляет вам ту же функцию изоляции, что и jest
, но без контекста vm
. Обратите внимание, что не все функции jest поддерживаются этим раннером, но все наиболее часто используемые функции работают!
Итак, после его установки вы можете убедиться, что тесты являются зелеными, выполнив команду:
jest --runner jest-light-runner test.js
Краткое содержание
jest
- отличный фреймворк для тестирования, и он хорошо работает для интерфейсных приложений, но вы можете столкнуться с некоторыми проблемами, если ваши зависимости зависят от instanceof
.
Мы обсуждали, как решить эту проблему различными способами:
- Примите пользовательскую тестовую среду
jest-environment-node-single-context
и ее ограничения. - Используйте раннер
jest-light-runner
, чтобы получить ту же функцию изоляцииjest
, но с подмножеством ее функций. - Откройте проблему в репозитории модуля, который вы используете, и попросите удалить оператор
instanceof
, что по-прежнему является хорошей практикой. - Выберите другую тестовую среду. Я лично предпочитаю
node-tap
Архитектура jest
имеет смысл для интерфейсных приложений, которые запускаются в браузере и имеют другой глобальный контекст, но она не подходит для Node.js приложения.
Мне не нравится концепция использования другого глобального контекста в моей тестовой и производственной среде.
Теперь перейдите к исходному коду этой статьи, чтобы попробовать фрагменты кода, которые я написал, чтобы проверить свои выводы.