Рефакторинг с уверенностью

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

Поскольку список констант, которые мне нужно было изменить, был довольно длинным, велика вероятность, что я допущу ошибку. Наши автоматические тесты, скорее всего, не обнаружат ошибку, поскольку наш файл constants.js не тестировался явно, поскольку он не содержит никакой логики. Поэтому, чтобы повысить свою уверенность при внесении изменений, я сначала создал (одноразовый) модульный тест. Хотя я обычно избегаю использования шутливых снимков, я думаю, что это был идеальный вариант их использования.

Изучение проблем

Давайте начнем с того, что взглянем на наш исходный объект констант:

const constants = {
  CHANNEL_BLOG: 1,
  CHANNEL_WORDPRESS: 2,
  CHANNEL_SHOPIFY: 3,
  CHANNEL_FACEBOOK: 5,
  CHANNEL_TWITTER: 6,
  
  CHANNEL_GROUP_PERSONAL: [1, 2, 3, 4],
  CHANNEL_GROUP_SOCIAL: [5, 6],
  CHANNEL_NAMES: {
    1: 'Blog',
    2: 'WordPress',
    3: 'Shopify',
    5: 'Facebook',
    6: 'Twitter',
  },
  CHANNEL_ROUTES: {
    1: 'channels/blog',
    2: 'channels/wordpress',
    3: 'channels/shopify',
    5: 'channels/facebook',
    6: 'channels/twitter',
  },
};

Вышеупомянутый объект является уменьшенной версией настоящего объекта, поэтому он более наглядно иллюстрирует все проблемы, которые я хотел исправить:

  • Значение каждого канала находится в трех или более местах. Это означает, что если нам потребуется изменить значение канала, мы должны сделать это во всех местах. Внимание, спойлер, вы, скорее всего, пропустите изменение одного из них.
  • Значения могут рассинхронизироваться, поскольку значения не связаны друг с другом. Возможно, вы заметили такую ​​проблему в приведенном выше примере. В какой-то момент кто-то удалил CHANNEL_ARTICLE со значением 4. Они удалили его определение, имя и маршрут; но забыл исключить его из всех групп каналов (см. CHANNEL_GROUP_PERSONAL).
  • Группы каналов трудно читать, так как вам нужно вручную/мысленно сопоставить значение с соответствующим именем канала. Это означает, что если вы хотите удалить канал Facebook из CHANNEL_GROUP_SOCIAL, вам сначала нужно выяснить, какое значение (5 или 6) относится к Facebook.

Внесение улучшений

Объект со всеми вышеперечисленными проблемами будет выглядеть примерно так:

const CHANNEL_BLOG = 1;
const CHANNEL_WORDPRESS = 2;
const CHANNEL_SHOPIFY = 3;
const CHANNEL_FACEBOOK = 5;
const CHANNEL_TWITTER = 6;
const constants = {
  CHANNEL_BLOG,
  CHANNEL_WORDPRESS,
  CHANNEL_SHOPIFY,
  CHANNEL_FACEBOOK,
  CHANNEL_TWITTER,
  
  CHANNEL_GROUP_PERSONAL: [
     CHANNEL_BLOG,
     CHANNEL_WORDPRESS,
     CHANNEL_SHOPIFY
  ],
  CHANNEL_GROUP_SOCIAL: [CHANNEL_FACEBOOK, CHANNEL_TWITTER],
CHANNEL_NAMES: {
    [CHANNEL_BLOG]: 'Blog',
    [CHANNEL_WORDPRESS]: 'WordPress',
    [CHANNEL_SHOPIFY]: 'Shopify',
    [CHANNEL_FACEBOOK]: 'Facebook',
    [CHANNEL_TWITTER]: 'Twitter',
  },
  CHANNEL_ROUTES: {
    [CHANNEL_BLOG]: 'channels/blog',
    [CHANNEL_WORDPRESS]: 'channels/wordpress',
    [CHANNEL_SHOPIFY]: 'channels/shopify',
    [CHANNEL_FACEBOOK]: 'channels/facebook',
    [CHANNEL_TWITTER]: 'channels/twitter',
  },
};

Давайте посмотрим на новый объект более внимательно:

  1. Мы преобразовали определения каналов в переменные. Это позволяет нам повторно использовать их в качестве значений для других констант.
  2. Мы заменили фиксированные значения внутри групп каналов. Это позволяет кому-то легко прочитать, какие каналы находятся в каждой группе. Ошибку, упомянутую выше, когда кто-то забыл удалить все значения константы, также невозможно будет повторить в будущем. Это связано с тем, что как только вы удалите константу и не удалите все ее использования, вы начнете видеть ошибку ReferenceError: «x не определен».
  3. Мы заменили фиксированные ключи свойств CHANNEL_NAMES и CHANNEL_ROUTES переменными канала. Мы сделали это, используя вычисляемые имена свойств.

Как видите, для каждой константы нужно внести несколько изменений. В случае с нашим приложением, где у нас более 30 каналов, велика вероятность, что я допущу ошибку при рефакторинге. Я надеюсь, что на этом этапе вы согласитесь с тем, что первоначальные инвестиции в написание модульного теста того стоят.

Создание модульного теста

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

Мы могли бы включить исходный объект в тест и использовать сопоставитель toMatchObject, но вместо этого я решил создать снимок:

import constants from '../constants';
it('should match the original object', () => {
   expect(constants).toMatchSnapshot();
});

Когда мы запускаем тест в первый раз, Jest запомнит значение объекта, сохранив его в виде снимка. Затем он будет использовать снимок для сравнения, когда мы снова запустим тест.

Для запуска теста я использовал следующую команду Jest CLI:

jest contants.test.js --watch

Режим --watch гарантирует автоматический запуск теста каждый раз, когда мы вносим изменения.

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

Заключение

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

Если изменение небольшое и изолированное (изменение имени переменной, исправление опечатки и т. д.), допустимым подходом может быть проверка изменения вручную.

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

  • большие изменения,
  • изменения, затрагивающие несколько частей,
  • изменения, которые требуют от вас тестирования многих случаев.

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

Первоначально опубликовано на https://zigamiklic.com 16 мая 2022 г.