Тестирование в React, часть 4: Фермент

Эта статья является частью серии о тестировании в React:

Тестирование в React, часть 1: Типы и инструменты

Тестирование в React, часть 2: Библиотека тестирования React

Тестирование в React, часть 3: Jest & Jest-Dom

Тестирование в React, часть 4. Фермент

Тестирование в React, Часть 5: Сквозное тестирование с Cypress

Тестирование в React, Часть 6: Тестирование в реальных условиях с помощью библиотеки тестирования React, Jest, Enzyme и Cypress

Я знаю, что сказал, что мы не собираемся освещать Enzyme, но сюрприз! Мы. Хотя я понимаю намерение тестирования пользовательского опыта, созданного кодом, а не фактической реализацией кода, эти два аспекта неразрывно связаны. Из-за этого я считаю, что Enzyme не является альтернативой React Testing Library, а скорее дополнительным инструментом для использования с ней, и поэтому на него стоит потратить некоторое время.

Библиотека тестирования ферментов и реакций

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

Например, чтобы отправить форму, пользователь будет искать кнопку с текстом «Отправить», нажимать на нее, а затем потенциально видеть какое-то сообщение «об успешном выполнении», отображаемое в DOM. Проверяя это с помощью библиотеки тестирования React, вы найдете кнопку, основанную на тексте «Отправить», щелкните по ней, а затем запросите текст этого сообщения «об успешном выполнении». Однако с помощью Enzyme вы можете проверить функциональность между сообщением «щелчок» и «успех».

Из-за этого фундаментального расхождения есть одно особенно важное отличие: Enzyme может напрямую тестировать состояние и манипулировать им, в то время как React Testing Library - нет. Это основная причина, по которой я предпочитаю использовать Enzyme вместе с другими моими инструментами для тестирования. Просто бывают случаи, когда вам нужно напрямую проверить состояние и напрямую проверить состояние обновления. При этом я обнаружил ошибки и несоответствия, которые, хотя и не повлияли на пользовательский опыт, не являются хорошим кодом и могут привести к негативным последствиям для UX в будущем.

Визуализация компонента

Ферментные тесты начинаются с фактического рендеринга компонента React, и для этого у вас есть три варианта: поверхностный рендеринг, полный рендеринг DOM и статический рендеринг. Мы рассмотрим первые два.

Мелкая отрисовка

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

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

import { shallow } from 'enzyme';
import MyComponent from './MyComponent';
it('renders the component title', () => {
   const wrapper = shallow(<MyComponent />);
   expect(wrapper.find('.header')).to.have.lengthOf(1);
});

Полная отрисовка DOM

Полная отрисовка DOM идеальна для случаев использования, когда у вас есть компоненты, которые могут взаимодействовать с API-интерфейсами DOM, или вам нужно тестировать компоненты, заключенные в компоненты более высокого порядка.

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

import { mount } from 'enzyme';
import MyComponent from './MyComponent';
it('calls componentDidMount', () => {
   const wrapper = mount(
      <MyComponent
        handleOnChange={handleOnChange}
        foo={bar}
      />
   );
   handleOnChange('baz');
   wrapper.update();
   expect(wrapper.props().foo).to.Equal('baz');
});

Справочные материалы по API

В документации Enzyme вы можете найти полный список вызовов, доступных вам для каждого типа рендеринга. Их можно использовать для обхода визуализированного компонента, утверждения чего-либо о визуализированном компоненте или управления визуализированным компонентом.

Обход

Обход позволяет вам находить различные элементы внутри компонента - элементы, которыми вы затем можете манипулировать или делать утверждения. На данный момент они должны показаться вам знакомыми по Jest и RTL:

  • contains(nodeOrNodes): принимает узел или узлы DOM и возвращает логическое значение в зависимости от того, присутствуют ли узел / узлы в визуализированной оболочке.
  • children(selector): необязательно принимает селектор и возвращает либо всех дочерних элементов текущей оболочки, либо всех дочерних элементов с данным селектором текущей оболочки.
  • find(selector): принимает селектор и возвращает все узлы в визуализированной оболочке, которые имеют данный селектор.
  • findWhere(fn): принимает функцию предиката (функция, которая возвращает логическое значение) и возвращает все узлы, для которых функция предиката равна true.
  • getElement(): возвращает завернутый элемент.
  • getElements(): возвращает завернутые элементы.
  • parent(): возвращает родительский узел текущей оболочки.
  • text(): возвращает строку визуализированного текста в обернутом компоненте. Следует использовать экономно, но можно использовать с такими вызовами, как contain().

Манипуляции

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

  • setContext(context): принимает объект контекста, устанавливает контекст корневого компонента и повторно отображает.
  • setProps(nextProps[, callback]): принимает объект с новыми реквизитами и дополнительной функцией обратного вызова, устанавливает реквизиты корневого компонента и повторно выполняет рендеринг.
  • setState(nextState[, callback]) / state(): принимает объект с новым состоянием и необязательной функцией обратного вызова, устанавливает состояние корневого компонента и выполняет повторный рендеринг. state() можно использовать для доступа к объекту состояния.
  • simulate(event[, mock]): аналогично fireEvent() в RTL, принимает имя события в виде строки (например, щелчок) и необязательный фиктивный объект, моделирует событие в корневом узле оболочки.
  • update(): обновляет дерево компонентов Enzyme в соответствии с деревом компонентов React.

Утверждения

Утверждения также должны выглядеть знакомыми на этом этапе и являются фактической проверкой элемента. Найдя элемент, вы можете сказать что-то вроде: «У него есть текст« Добро пожаловать в мое приложение! »».

  • contains(nodeOrNodes): принимает узел или узлы, возвращает логическое значение в зависимости от того, содержит ли оболочка данный узел / узлы.
  • equals(node): принимает узел, возвращает логическое значение в зависимости от того, равна ли оболочка переданному узлу.
  • hasClass(className): принимает имя класса как строку, возвращает логическое значение в зависимости от того, имеет ли обернутый узел данное имя класса.
  • isEmpty(): возвращает логическое значение в зависимости от того, пуста ли оболочка.
  • some(selector): принимает селектор, возвращает логическое значение в зависимости от того, содержат ли какие-либо узлы внутри оболочки данный селектор.

Еще кое-что

Выше много говорится о «обернутом узле» и «обертке». Для ясности, это может быть любой завернутый узел, возвращаемый из вызова, а не только начальная переменная wrapper, обычно объявляемая и назначаемая визуализируемому компоненту. Это означает, что вы можете пройти по визуализированному компоненту, чтобы найти узел, который хотите протестировать, назначить найденную оболочку узла / узлов новой переменной, а затем использовать вышеуказанные вызовы в этой новой оболочке:

import { mount } from 'enzyme';
import MyComponent from './MyComponent';
it('displays a header with text "Welcome!"', () => {
   const wrapper = mount(<MyComponent />);
   // Wraps the header node in a new wrapper on which to make calls
   const header = wrapper.find('.header');
   expect(header).hasClass('header');
   expect(header).to.equal('<div className="header">Welcome!</div>')
});

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

Предыдущий: Тестирование в React, часть 3: Jest и Jest-Dom

Далее: Тестирование в React, часть 5: Сквозное тестирование с Cypress

Ресурсы