Запуск нашего набора тестов с Mocha занял 12+ минут. В CI с нашими мощными машинами сборки теперь мы можем запустить весь пакет Jest за 4 минуты 30 секунд.

Мы использовали Mocha на Airbnb с сентября 2013 года, но из-за растущих проблем мы недавно перешли с Mocha на Jest. На самом деле миграция потребовала минимальных изменений в наших тестах и ​​инфраструктуре и дала множество преимуществ.

Минимальные изменения в тестах

Мы приложили значительные усилия для того, чтобы наши тесты соответствовали передовым методам и довольно строгому стилю кодирования. Это помогло нам уменьшить нестабильность и обеспечить межгрупповое сотрудничество, потому что тесты выглядят одинаково в разных разделах нашего кода. Для нас было очень важно, чтобы наши тестовые файлы выглядели более или менее одинаково до / после миграции.

Большинству команд не нужно будет изменять содержимое своих тестовых файлов. Фактически, приведенного ниже фрагмента было достаточно на Airbnb, чтобы скрыть различия между тем, как мы используем Mocha и Jest.

Это возможно, потому что Jest API и Mocha API похожи, только лишь немного отличаются имена функций. Какие функции вам нужно контролировать (или изменять в тестах), во многом зависит от того, какие функции вы используете. У нас также было несколько test вызовов, которые в соотношении 1: 1 можно было заменить на it, что мы решили как стандарт. Ниже приведен краткий пример того, как выглядят наши тесты до и после перехода на Jest.

Упрощенная архитектура тестирования

Изначально мы развернули покрытие кода с помощью Istanbul и Mocha в январе 2016 года, но мы обнаружили, что инструменты для наших исходных файлов были дорогостоящими, и добавили неоправданно много времени на наши тесты.

Чтобы решить эту проблему, мы написали некоторую настраиваемую логику, чтобы разбивать наши тесты на блоки, запускать их в отдельных процессах, собирать покрытие для каждого, а затем объединять покрытие в один отчет в конце.

Покрытие было собрано (и обеспечено) в другой работе CI, чем наш набор тестов. Позже сам набор тестов был распараллелен путем отправки фрагментов набора тестов на разные рабочие машины и агрегирования результатов в конце.

Jest автоматически разделяет тесты по процессам, собирая и объединяя покрытие. Для нас это было большим преимуществом. Воспользовавшись этим фактом, мы смогли удалить нашу настраиваемую логику в обеих задачах и полагаться исключительно на Jest, чтобы сделать это разумно для нас.

Улучшенная производительность

Для проектов с большим количеством тестовых файлов Jest сразу же повысит производительность. Это можно сделать с помощью пары механизмов.

  1. Распараллеливание. Вероятно, это то, чему Jest уделяют больше всего внимания, и не зря. Если вы еще не распараллелили свою работу, связанную с процессором, вы можете ожидать большого прироста производительности от этого.
  2. Как было сказано выше, мы уже распараллеливали наши тесты в Airbnb, но мы делали это, получая список всех наших тестовых файлов и деля их поровну между нашими сотрудниками. Это оставляло работнику возможность получить либо аномально быструю, либо аномально медленную очередь тестов и приводило к потере циклов ЦП. Вместо этого Jest использует циклический подход и сначала запускает самые медленные тесты, помогая вам выжать максимум из вашей вычислительной мощности.
  3. Он имеет встроенный кеш-трансформацию babel. Применение преобразований к коду очень сильно нагружает процессор. Используя кеш, который является общим для всех процессов, вы можете выделить свой процессор для выполнения кода и сократить много времени на выполнение.

С Mocha нашему пакету требовалось ~ 45 минут для локального запуска, а иногда он вообще не мог быть завершен из-за нехватки памяти при запуске всего пакета в одном потоке. С Jest это время сокращается до 14,5 минут на местном уровне. Мы увидели аналогичное улучшение на нашем сервере сборки: Mocha показала его более 12 минут (после нашей работы по распараллеливанию между машинами), а Jest завершился за 4,5 минуты.

Уменьшение шелушения

Когда у вас есть такой большой набор тестов, как наш (несколько тысяч тестовых файлов), выполнение ваших тестов в одном потоке неизбежно приведет к нестабильности. Когда мы начали работать над миграцией, примерно 12% наших сборок нужно было перезапустить из-за flake, и у нас были тесты в нашем наборе, которые требовали, чтобы другие тесты запускались первыми, иначе они не прошли бы.

Изолированный запуск означает, что тест не может завершиться неудачно из-за побочных эффектов других тестовых файлов в вашем наборе. Это особенно полезно для ошибок, которые возникают в setTimeout вызовах после завершения теста. Теперь нам намного проще исследовать нестабильный тест, проверяя тест и исходный файл на наличие асинхронного кода.

После перехода на Jest и исправления тестов, которые не давали результата по отдельности, мы смогли снизить уровень отслаивания до ~ 1%. Это экономит нашим разработчикам часы рабочего дня, поскольку им больше не нужно ждать, пока сборка не завершится, и многократно повторно запускать набор тестов, пока он не пройдет. Кроме того, в тех редких случаях, когда отслаивание действительно происходит, мы можем более точно определить его источник. Его легче идентифицировать, потому что файл запускается в собственном процессе, поэтому гарантируется, что нестабильность исходит изнутри этого файла. С Mocha неправильный таймер в файле x может привести к сбою теста в файле y.

Если вы стремитесь уменьшить нестабильность, вы можете сделать еще один шаг и еще больше уменьшить расслоение в ваших тестах, убрав таймеры, установленные в ваших тестах:

Повышение производительности на шаг впереди

Шутка была для нас намного быстрее, но изначально мы не заметили ожидаемых улучшений. После нескольких прогонов профилирования мы обнаружили, что виноват наш глобальный файл spec_helper.js. Это файл, который мы создали с помощью Mocha для настройки некоторых глобальных помощников, которые сделали наши тесты более удобными для написания. Например, мы используем Enzyme для тестирования нашего кода React, а чтобы облегчить написание тестов, мы включаем chai -etherme. Вместо того, чтобы заставлять всех наших разработчиков вручную подключать эту библиотеку в каждый тестовый файл, мы подключаем ее в spec_helper.js, который запускается перед всеми нашими тестами.

В Jest это оказывается действительно проблематичным. Поскольку каждый тестовый файл запускается на чистой виртуальной машине, Jest повторно запускает spec_helper.js файл один раз для каждого тестового файла. В случае вышеприведенного примера импорт chai-фермента запускает цепочку, которая импортирует весь фермент, который затем импортирует все React и ReactDOM. Это занимает 480 мс даже для тестов, которые не включают React. В нашем случае 480 мс * несколько тысяч файлов означают, что мы потратили больше минуты на настройку этой библиотеки. С Mocha мы не почувствовали боли, потому что он не распараллеливается и запускает spec_helper.js файл только один раз.

Чтобы обойти это, мы проявили немного изобретательности, применив насмешливые возможности Jest. Используя обратный вызов на jest.mock(), мы смогли перехватить импорт ферментов и загрузить chai-фермент только для тех тестов, которые в этом нуждаются.

Мнение разработчиков

Нашей конечной целью при этой миграции было улучшить опыт разработчиков как при написании их тестов, так и при их запуске. Мы были на Jest всего несколько недель, но пока не получили ничего, кроме положительных отзывов:

Просто хотел прийти сюда и дать большой ++ всем, кто работал над реализацией Jest. Это невероятно экономит время на разработку. Например, то, на что раньше уходило 3 часа, теперь занимает около 30 минут. Не шутка.

Мы всегда ищем талантливых, любознательных людей, чтобы влиться в команду. Или, если вы просто хотите поговорить о магазине, в любое время напишите мне в твиттере @garyborton.