Реактивное юнит-тестирование с использованием фермента и Jest

  1. Зачем использовать TDD для создания компонента React.js?
  2. Как выполнить модульное тестирование компонента React.js?
  3. Использование Jest для создания юнит-тестов
  4. Использование Enzyme для монтирования компонентов React.js
  5. Настройка нашей среды
  6. Создание компонента React.js с использованием TDD
  7. Резюме

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

В то время как есть много инструментов, доступных для JavaScript и React.js модульное тестирование. В этом посте мы будем использовать Jest и Enzyme для создания компонента React.js с базовыми функциональными возможностями с использованием TDD.

Зачем использовать TDD для создания компонента React.js?

TDD дает много преимуществ вашему коду - одно из преимуществ высокого охвата тестов состоит в том, что он позволяет легко выполнять рефакторинг кода, сохраняя при этом ваш код чистым и функциональным.

Если вы создали компонент React.js ранее, вы поняли, что код может расти очень быстро. Он заполняется множеством сложных условий, вызванных утверждениями, связанными с изменениями состояния и вызовами службы.

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

Как выполнить модульное тестирование компонента React.js?

Существует много стратегий, которые мы можем использовать для тестирования компонента React.js:

  • Мы можем убедиться, что определенная функция в props была вызвана при отправке определенного события.
  • Мы также можем получить результат функции рендера с учетом текущего состояния компонента и сопоставить его с предопределенным макетом.
  • Мы даже можем проверить, соответствует ли число дочерних элементов компонента ожидаемому количеству.

Чтобы использовать эти стратегии, мы собираемся использовать два инструмента, которые пригодятся для работы с тестами в React.js: Шутка а также энзим ,

Использование Jest для создания юнит-тестов

Jest - это тестовая среда с открытым исходным кодом, созданная Facebook, которая прекрасно интегрируется с React.js. Он включает в себя инструмент командной строки для выполнения теста, аналогичный тому, что предлагают Jasmine и Mocha. Это также позволяет нам создавать фиктивные функции с почти нулевой конфигурацией и предоставляет действительно хороший набор сопоставителей, который облегчает чтение утверждений.

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

Использование Enzyme для монтирования компонентов React.js

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

Enzyme предлагает две основные функции для монтажа компонентов: мелкий и крепление. Функция shallow загружает в память только корневой компонент, тогда как mount загружает полное дерево DOM.

Мы собираемся объединить Enzyme и Jest для монтирования компонента React.js и выполнения над ним утверждений.

Настройка нашей среды

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

Мы используем следующие версии:

{"реагировать": "16.0.0", "фермент": "^ 2.9.1", "jest": "^ 21.2.1", "jest-cli": "^ 21.2.1", "babel-jest ":" ^ 21.2.0 "}

Создание компонента React.js с использованием TDD

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

// MyComponent.test.js import React from 'реагировать; импорт {неглубокий} из «фермента»; импортировать MyComponent из './MyComponent'; description ("MyComponent", () => {it ("должен визуализировать мой компонент", () => {const wrapper = shallow (<MyComponent />);});});

После запуска теста мы получаем следующую ошибку:

ReferenceError: MyComponent не определен.

Затем мы создаем компонент, обеспечивающий базовый синтаксис для прохождения теста.

// MyComponent.js import React из'act '; экспортный класс по умолчанию MyComponent extends React.Component {render () {return <div />; }}

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

После вызова этого метода Jest автоматически создает файл снимка с именем [testFileName] .snap, в который добавляется папка __snapshots__.

Этот файл представляет макет пользовательского интерфейса, который мы ожидаем от рендеринга нашего компонента.

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

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

У вас может возникнуть желание сначала выполнить функцию toMatchSnapshot и увидеть результат в файле снимка, и это допустимая опция. Однако, если мы действительно хотим использовать чистый TDD, нам нужно узнать, как структурированы файлы снимков.

Файл снимка содержит макет, который соответствует названию теста. Это означает, что если наш тест имеет такую ​​форму:

desc ("ComponentA" () => {it ("должно что-то сделать", () => {…}});

Мы должны указать это в разделе экспорта: компонент А должен что-то сделать 1.

Вы можете прочитать больше о тестировании снимков Вот ,

Итак, сначала мы создаем файл MyComponent.test.js.snap.

//__snapshots__/MyComponent.test.js.snap export [`MyComponent должен отображать исходный макет 1`] =` Array [<div> <input type = "text" /> </ div>,] ``;

Затем мы создадим модульный тест, который проверит, соответствует ли моментальный снимок дочерним элементам компонента.

// MyComponent.test.js ... it («должен отображать исходный макет», () => {// когда const component = shallow (<MyComponent />); // тогда ожидаем (component.getElements ()). toMatchSnapshot ();}); ...

Мы можем рассматривать component.getElements как результат метода рендеринга.

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

После выполнения теста мы получаем следующую ошибку:

Полученное значение не соответствует сохраненному снимку 1. Ожидается: - Массив [<div> <input type = "text» /> </ div>,] Фактический: + Array []

Jest говорит нам, что результат из component.getElements не соответствует снимку. Итак, мы выполняем этот тест, добавляя элемент ввода в MyComponent.

// MyComponent.js import React из'act '; класс по умолчанию для экспорта MyComponent extends React.Component {render () {return <div> <input type = "text" /> </ div>; }}

Следующим шагом является добавление функциональности к вводу путем выполнения функции при изменении ее значения. Мы делаем это, определяя функцию в пропозиции onChange.

Сначала нужно изменить снимок, чтобы тест не прошел.

//__snapshots__/MyComponent.test.js.snap export [`MyComponent должен отобразить начальный макет 1`] =` Array [<div> <input onChange = {[Function]} type = "text" /> </ div>, ] `;

Недостатком модификации снимка в первую очередь является то, что важен порядок реквизита (или атрибутов).

Jest отсортирует в алфавитном порядке реквизиты, полученные в функции ожидаемого, прежде чем проверять их по снимку. Итак, мы должны указать их в этом порядке.

После выполнения теста мы получаем следующую ошибку:

Полученное значение не соответствует сохраненному снимку 1. Ожидается: - Массив [<div> onChange = {[Функция]} <тип ввода = «текст» /> </ div>,] Фактический: + Массив [<div> <тип ввода = ”Текст” /> </ div>,]

Чтобы пройти этот тест, мы можем просто предоставить пустую функцию для onChange.

// MyComponent.js import React из'act '; класс по умолчанию для экспорта MyComponent extends React.Component {render () {return <div> <input onChange = {() => {}} type = "text" /> </ div>; }}

Затем мы гарантируем, что состояние компонента изменяется после отправки события onChange.

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

Затем мы проверяем, что состояние компонента содержит ключ с именем input.

// MyComponent.test.js ... it («должен создать запись в состоянии компонента», () => {// данный const component = shallow (<MyComponent />); const form = component.find ('input '); // когда form.props (). onChange ({target: {name:' myName ', значение:' myValue '}}); // тогда ожидать (component.state (' input ')). toBeDefined ( );});

Теперь мы получаем следующую ошибку.

Ожидаемое значение будет определено, вместо этого получено неопределенное

Это указывает на то, что компонент не имеет свойства в состоянии, называемом вводом.

Мы выполняем тестирование, устанавливая эту запись в состоянии компонента.

// MyComponent.js import React из'act '; класс по умолчанию для экспорта MyComponent extends React.Component {render () {return <div> <input onChange = {(событие) => {this.setState ({input: ''})}} type = "text" /> </ DIV>; }}

Затем нам нужно убедиться, что значение задано в новой записи состояния. Мы получим это значение из мероприятия.

Итак, давайте создадим тест, который удостоверится, что состояние содержит это значение.

// MyComponent.test.js ... it («должен создать запись в состоянии компонента со значением события», () => {// данный const component = shallow (<MyComponent />); const form = component. find ('input'); // когда form.props (). onChange ({target: {name: 'myName', значение: 'myValue'}}); // затем ожидать (component.state ('input') ) .toEqual ('myValue');}); ~~~ Не удивительно, мы получаем следующую ошибку. ~~ Ожидаемое значение равно: "myValue" Получено: ""

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

// MyComponent.js import React из'act '; класс по умолчанию для экспорта MyComponent extends React.Component {render () {return <div> <input onChange = {(событие) => {this.setState ({input: event.target.value})}} type = "text" / > </ DIV>; }}

Убедившись, что все тесты пройдены, мы можем реорганизовать наш код.

Мы можем извлечь функцию, переданную в подпорке onChange, в новую функцию с именем updateState.

// MyComponent.js import React из'act '; класс по умолчанию для экспорта MyComponent extends React.Component {updateState (event) {this.setState ({input: event.target.value}); } render () {return <div> <input onChange = {this.updateState.bind (this)} type = "text" /> </ div>; }}

Теперь у нас есть простой компонент React.js, созданный с использованием TDD.

Резюме

В этом примере мы попытались использовать чистый TDD, выполняя каждый шаг, записывая как можно меньше кода, чтобы не пройти и пройти тесты.

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

Использование менее строгого процесса TDD также допустимо и может работать просто отлично.

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

Если вы хотите узнать больше о TDD и связанной с этим поведенческой разработке (BDD), прочитайте Ваш босс не ценит TDD Товарищ Toptaler Райан Уилкокс.

Js?
Js?
Js?
Js?