На работе мы выбрали Jest в качестве предпочтительного фреймворка для модульного тестирования JS.

Ранее в некоторых проектах в качестве комбинированной тестовой среды использовались Mocha, Chai, Sinon, Karma и PhantomJS, и теперь они переносятся на Jest.

Зачем мигрировать?

На наш переход на Jest повлияло несколько факторов, в том числе:

  • jsdom быстрее и легче, чем phantomjs
  • Мы обнаружили неперехваченные синтаксические ошибки в тестовых наборах Mocha (например, недопустимое использование установки / разрыва), которые могут привести к «тихому» пропуску большого количества тестов, например. нет ошибки
  • mocha не поддерживает тестирование снимков
  • Наша существующая установка включает большое количество зависимостей, которые могут оказаться более сложными для управления совместимостью версий.
  • Разработка безголового браузера phantomjs была приостановлена ​​в марте 2018 года

Обновление зависимостей

После установки jest и связанных модулей наши существующие зависимости тестовой среды в package.json-

"devDependencies": {
  "@babel/core": "7.2.2",
  "babel-plugin-istanbul": "3.0.0",
  "chai": "3.4.1",
  "chai-as-promised": "6.0.0",
  "chai-enzyme": "1.0.0-beta.0",
  "enzyme": "3.3.0",
  "enzyme-adapter-react-16": "1.1.1",
  "eslint-plugin-chai-friendly": "0.4.1",
  "eslint-plugin-mocha": "4.11.0",
  "karma": "1.0.0",
  "karma-babel-preprocessor": "6.0.1",
  "karma-chai": "0.1.0",
  "karma-coverage": "1.0.0",
  "karma-mocha": "1.0.1",
  "karma-mocha-reporter": "2.0.0",
  "karma-phantomjs-launcher": "1.0.2",
  "karma-sourcemap-loader": "0.3.7",
  "karma-webpack": "2.0.2",
  "mocha": "3.0.1",
  "sinon": "2.3.7",
  "sinon-chai": "2.8.0"
}

теперь сокращены до -

"devDependencies": {
  "@babel/core": "7.2.2",
  "babel-jest": "23.6.0",
  "enzyme": "3.3.0",
  "enzyme-adapter-react-16": "1.1.1",
  "enzyme-to-json": "3.3.3",
  "eslint-plugin-jest": "21.15.0",
  "jest": "23.1.0"
}

enzyme сохранен для тестирования компонентов React с добавлением enzyme-to-json для преобразования оболочек Enzyme в формат JSON для совместимости с тестированием моментальных снимков Jest. enzyme-to-json также необходимо настроить в поле snapshotSerializers в jest.config.js -

module.exports = {
  projects: [
    {
      displayName: 'tests',
      snapshotSerializers: ['enzyme-to-json/serializer']
    }
  ]
}

Утверждения в chai заменены на Jest expect. Аналогичным образом chai-as-promised заменяется сопоставителями Jest .resolves / .rejects для тестирования асинхронного кода.

Шпионы, заглушки и моки в sinon заменены на Jest для имитации функций (API), модулей и классов.

В тестах, которые раньше выполнялись в безголовом браузере phantomjs, теперь используется jsdom (по умолчанию с Jest). Обратите внимание, что Jest в настоящее время поддерживает только jsdom в качестве целевой среды. Если у вас есть тесты, которые требуют реальной среды браузера, Jest можно использовать вместе с Puppeteer или Electron для поддержки этого.

JS-компиляция с karma-babel-preprocessor заменяется на babel-jest.

Для покрытия кода мы продолжаем использовать istanbul - ранее добавленное через karma-coverage, по умолчанию с Jest.

Средство выполнения тестов karma заменено на jest CLI.

Перенос существующих тестов

jest-codemods - это рекомендуемая отправная точка для автоматизации преобразования тестовых файлов из вашей существующей инфраструктуры в Jest.

Чтобы установить и запустить мод, выполните следующие команды в корневом каталоге вашего проекта -

npm i -g jest-codemods
jest-codemods

затем выполните серию подсказок -

Из какой тестовой библиотеки вы хотите перейти?

  • Chai: Should/Expect BDD Syntax при использовании синтаксиса Chai expect или should
  • Chai: Assert Syntax при использовании синтаксиса Chai assert
  • Mocha при использовании синтаксиса Mocha assert

Используете ли вы глобальный объект для утверждений (т. е. не требуя их)

  • Yes, если ваша тестовая среда делает утверждения доступными в глобальном пространстве имен через установочный файл, например. global.expect = chai.expect
  • No если вы import или require утверждения в своих тестовых файлах

Будете ли вы использовать Jest на Node.js в качестве средства выполнения тестов?

  • Yes, используйте глобальные переменные, предоставленные Jest (рекомендуется)

К каким файлам или каталогам следует применить codemods?

  • Введите путь (пути) к файлу / каталогу для тестовых файлов, которые вы хотите перенести. Если у вас большой набор тестов и вам может потребоваться выполнить миграцию в несколько этапов, рекомендуется настроить таргетинг только на путь (пути) к файлу / каталогу, необходимый для этого этапа.

Теперь модификация начнется и по завершении вернет сводку -

All done.
Results:
2 errors
0 unmodified
31 skipped
346 ok
Time elapsed: 8.512 seconds

Проверка тестового файла (ов) покажет, что большинство утверждений теперь используют синтаксис Jest. Большой!

Однако в сводке отмечается, что несколько тестовых файлов вызвали ошибки или были пропущены -

2 errors
31 skipped

и запуск jest приводит к множеству новых неудачных тестов…

Исправление оставшихся тестов и конфигурации вручную…

В случае нашего проекта jest-codemods не может конвертировать тесты, используя -

  • sinon
  • sinon-chai
  • chai-as-promised

Переход от PhantomJS (безголовый браузер) к JSDOM (реализация JS DOM) привел к новым сбоям в тестах с использованием -

  • document или window API браузера

Кроме того, в нашем проекте используется webpack, и у Jest изначально возникают проблемы с анализом файлов, которые используют -

Вот как решить эти проблемы…

sinon / sinon-chai

Поскольку jest-codemods не поддерживает sinon, вам нужно будет вручную заменить его использование, чтобы вместо этого использовать Jest API. Это хорошее изложение синтаксических различий между ними.

chai-as-promised

Может быть заменен на сопоставители Jest .resolves / .rejects для тестирования асинхронного кода.

document или window API браузера

Тесты с использованием этих API потребуют полифиллов. Если такие API-интерфейсы браузера широко используются в вашем наборе тестов, возможно, имеет смысл добавить их в setupFiles Jest, чтобы они были доступны для всех тестов. Например, чтобы добавить полифилл window.matchMedia -

jest.config.js

module.exports = {
  projects: [
    {
      displayName: 'tests',
      setupFiles: ['./jest.setup.js']
    }
  ]
}

jest.setup.js

require('matchmedia-polyfill')
require('matchmedia-polyfill/matchMedia.addListener')

Однако стоит отметить, что setupFiles будет запускаться один раз для каждого тестового файла и может увеличить время выполнения теста.

Статические ресурсы

Поскольку тесты обычно не слишком связаны со статическими активами, их можно высмеять -

jest.config.js

module.exports = {
  'moduleNameMapper': {
    '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/mocks/fileMock.js',
    '\\.(css)$': '<rootDir>/mocks/styleMock.js'
  }
}

mocks/styleMock.js

module.exports = {}

mocks/fileMock.js

module.exports = ‘test-file-stub’

Модули CSS

То же, что и выше, но используйте identity-obj-proxy как макет -

npm i — dev identity-obj-proxy

jest.config.js

module.exports = {
  'moduleNameMapper': {
    '\\.(scss)$': 'identity-obj-proxy'
  }
}

Поэтапный переход

В зависимости от размера вашего набора тестов и / или процента файлов, требующих ручного исправления, вы можете выбрать миграцию набора тестов в несколько этапов, и, следовательно, вам нужно будет запускать обе среды тестирования до завершения. В jest.config.js файлы / каталоги могут быть включены (testMatch) или исключены (testPathIgnorePatterns), чтобы тесты выполнялись только с использованием правильной среды тестирования.

Резюме

Jest предоставляет всестороннюю, универсальную среду тестирования, и jest-codemods является отличной отправной точкой для переноса в нее существующего проекта.

Если вы ввели дополнительные библиотеки (например, sinon, sinon-chai, chai-as-promised) в дополнение к вашей основной структуре утверждений (например, chai или mocha), ожидайте, что ручное обновление этих тестов, вероятно, будет составлять подавляющее большинство времени, потраченного на миграцию в целом.

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