Enzyme мертв — вот как вы можете мигрировать

Enzyme мертв — вот как вы можете мигрировать

27 января 2023 г.

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

<цитата>

Если в вашем проекте используется React+Enzyme и вы мечтаете обновить версию React до версии 18 (и пользоваться такими интересными функциями, как серверный и параллельный рендеринг), то у меня для вас плохие новости  — Enzyme мертв, и не будет выпущено официальных адаптеров, совместимых с будущими версиями React (вы все еще можете найти некоторые неофициальные библиотеки, но они не будут им доверять).

МИССИЯ этой статьи состоит не только в том, чтобы объяснить подход, который вы можете использовать для переноса своего тестового кода на новую библиотеку, но и в том, чтобы предложить идею того, как вы можете автоматизировать мониторинг хода миграции.< /p>

Пора переходить!

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

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

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

Вот пример того, как вы можете подойти к поэтапной миграции с Enzyme на новую библиотеку, например Библиотека тестирования React (RTL):

  1. Начните с определения тестов в базе кода, которые используют Enzyme. Обычно это тесты, которые импортируют Enzyme или специфичные для Enzyme методы (например, shallow или mount).
  2. Начните с переноса небольшого набора этих тестов на RTL. Это может быть компонент или набор компонентов, с которыми вы уже знакомы, или раздел кодовой базы, который не имеет большого количества зависимостей.
  3. При переносе каждого теста обращайте внимание на структуру теста и его взаимодействие с тестируемым компонентом. Обратите внимание на любые различия в том, как новая библиотека обрабатывает такие вещи, как запросы элементов или моделирование событий.
  4. По мере переноса большего количества тестов вы начнете лучше понимать, чем RTL отличается от Enzyme. Воспользуйтесь этой возможностью обучения, чтобы реорганизовать свои тесты и улучшить их общую структуру и удобочитаемость.
  5. Повторяйте шаги 2–4, пока все ваши тесты не будут использовать новую библиотеку.
  6. И наконец, после завершения переноса обязательно запустите набор тестов, чтобы убедиться, что все работает должным образом.

В целом, инкрементная миграция — отличный способ отказаться от Enzyme и перейти на новую библиотеку тестирования. Применяя пошаговый подход, вы можете свести к минимуму риск поломки, изучить новую библиотеку по ходу дела и сделать общий процесс миграции более управляемым. Однако трудно понять, успешен ли этот подход, не зная метрик, описывающих успех. Как технический руководитель команды, как вы можете знать, что команда следует стратегии, которую вы придумали, и как далеко до конца ( шаг № 6)?

TLDR; Ссылка на код

Репозиторий GitHub: https://github.com/sr-shifu/eslint-plugin-enzyme- устаревание

Напишите плагин для отслеживания прогресса!

Какой инструмент вы обычно используете для обеспечения соблюдения стиля написания кода и поиска потенциальных ошибок в своем проекте? Вы, наверное, правильно поняли  —  ESLint! (Если вы ответили TSLint, вы немного отстали!). ESLint легко настраивается, вы можете устанавливать свои собственные правила, использовать свой форматтер или комбинировать оба в одном плагине! Его также можно легко интегрировать в рабочий процесс разработки, например в конвейер непрерывной интеграции, чтобы автоматически сообщать о любых проблемах до того, как они будут зафиксированы.

Написание собственного плагина ESLint не так сложно, как вы думаете, но вам может потребоваться больше времени для более глубокого изучения абстрактного синтаксического дерева (древовидное представление абстрактной синтаксической структуры исходного кода, написанного на языке программирования) и ESLint селекторы для обхода этого дерева. Без лишних слов позвольте мне представить свое решение:

const noShallowRule = require("./rules/no-shallow");
const noMountRule = require("./rules/no-mount");

const rules = {
  "no-shallow": noShallowRule,
  "no-mount": noMountRule,
};
module.exports = {
  rules,
  configs: {
    recommended: {
      rules,
    },
  },
};

КОНЕЦ!

.

.

.

.

Шучу! Теперь самое интересное!

Правила есть правила

Давайте углубимся в код правила. Оба правила no-shallow и no-mount используют одну и ту же логику (идея разделить их на части просто для того, чтобы дать пользователям больше гибкости в отношении того, что они хотят устареть), поэтому давайте углубимся в один из них (я выбрал shallow):

const schema = require("./schema");
const astUtils = require("ast-utils");

const resolveEnzymeIdentifierInScope = (scope, name) => {
  if (!scope) {
    return false;
  }
  const node = scope.set.get(name);
  if (node != null) {
    const nodeDef = node.defs[0];
    if (
      nodeDef.type === "ImportBinding" &&
      nodeDef.parent.source.value === "enzyme"
    ) {
      return true;
    }

    if (
      astUtils.isStaticRequire(nodeDef.node.init) &&
      astUtils.getRequireSource(nodeDef.node.init) === "enzyme"
    ) {
      return true;
    }
  }

  return false;
};

module.exports = {
  meta: {
    messages: {
      noShallowCall: "Enzyme is deprecated: do not use shallow API.",
    },
    docs: {
      description: "Disallow Enzyme shallow rendering",
      category: "Tests",
      recommended: true,
    },
    schema,
    fixable: null,
  },

  create(context) {
    const [options = {}] = context.options || [];
    return {
      "CallExpression"(node) {
        if (
          node.callee.name !== "shallow" &&
          node.callee.property?.name !== "shallow"
        ) {
          return;
        }
        let targetDeclarationName = "shallow";
        if (node.callee.property?.name === "shallow") {
          targetDeclarationName = node.callee.object.name;
        }
        const resolved = context
          .getScope()
          .references.find(
            ({ identifier }) => identifier.name === targetDeclarationName
          ).resolved;
        const isEnzyme = resolveEnzymeIdentifierInScope(
          resolved?.scope,
          targetDeclarationName
        );
        if (isEnzyme || options.implicitlyGlobal) {
          context.report({ node, messageId: "noShallowCall" });
        }
      },
    };
  },
};

* CallExpression — это селектор Enzyme, который сообщает Enzyme, что нас интересуют только вызовы функций. Эти селекторы очень похожи на селекторы CSS, вы можете узнать о них больше здесь. * node.callee.name ссылается на имя вызываемой функции (в нашем случае мелкой), а node.callee.property?.name проверяет, была ли эта функция вызывается как свойство объекта более высокого порядка (например, const энзимапи = require('энзим'); энзимапи.шаллоу(<Component />). * context.getScope() дает ссылку на область, в которой была вызвана целевая функция (shallow), и имеет ссылку на объект, которому принадлежит этот неглубокий метод. По сути, здесь нам нужно проверить, что поверхностный метод относится к исходному коду фермента — обычно фермент импортируется в тестовый модуль или требуется, если вы используете CommonJS (если вам интересно, как вы можете написать библиотеку, которая создает сборку для модули EcmaScript и цели CommonJS, перейдите к этой статье). * options.implicitlyGlobal — это параметр, который может быть предоставлен потребителем правила, например, в файле конфигурации .eslintrc.js. В этом конкретном примере это позволяет пользователям указать правилу, что они не заинтересованы в источнике, из которого мелкое исходит из (возможно, вы назначили его глобальной области видимости где-то в потоке настройки теста — плохая идея, ИМХО).

Сообщите о своем прогрессе

Для смельчаков, которые добрались до этой части, спасибо, и продолжим! Теперь у нас есть правила, которые мы можем использовать, чтобы предотвратить отправку устаревших API как части нового и измененного кода в ваши PR (надеюсь, вы используете findRelatedTests API как часть процесса предварительной фиксации Git).

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

Я не хочу выкладывать сюда огромные блоки кода  — вы можете найти его на моем GitHub repo, но я попытаюсь вкратце объяснить, как это работает. После запуска правил и сбора ошибок для каждого тестового файла плагин ESLint передает эти метаданные форматтеру:

{ 
    filePath: string; 
    messages: Array<{ruleId: string;}> 
}

В средстве форматирования мы группируем эти данные по пути к файлу и идентификатору нарушенного правила (например, enzyme-deprecation/no-shallow) и передаем эти обработанные данные визуализатору, который может выводить эти данные в разных форматах. форматы. Несколько идей о том, какими могут быть эти форматы:

* Печатные графики ASCII в терминале (для работы в локальной среде/среде разработки) * Файл на основе Markdown, записанный в файловую систему и отправленный в репозиторий Git (для просмотра прогресса после каждого отдельного PR) * HTML-страница с использованием красивых библиотек диаграмм (например, D3.js), записанная в папку coverage (при условии, что у вас уже могут быть некоторые интеграции с этой папкой в ​​вашем инструменте проверки кода) * Простое строковое сообщение передается на какой-либо URL-адрес веб-перехватчика (например, уведомление Slack на канал проверки кода)

Как использовать его в своем проекте?

Вариант 1. Определите отдельную конфигурацию ESLint для переноса

.eslintrc.migration.js:

module.exports = {
  parser: '<your-parser>',
  extends: ['plugin:enzyme-deprecation/recommended'],
  env: {
    browser: true,
  },
  rules: {
    'enzyme-deprecation/no-shallow': 2
  }
};

И в файле package.json определите команду:

"track:migration": "NODE_ENV=development eslint --no-eslintrc --config .eslintrc.migration.js -f node_modules/eslint-plugin-enzyme-deprecation/lib/formatter --ext .test.jsx src/"

Вариант 2. Использование Node.js API

Вы можете найти пример здесь < em>(запустить команду npm run demo в корневом каталоге)

Заключительные слова

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

:::информация Первоначально опубликовано по адресу https://thesametech.com 16 января 2023 г.

:::

Вы также можете подписаться на меня в Twitter и < em>подключитесь к LinkedIn чтобы получать уведомления о новых сообщениях!


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE