Статьи

Тестирование компонентов в React с использованием Jest: основы

Конечный продукт
Что вы будете создавать

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

Однако есть несколько причин, по которым я считаю, что вы должны протестировать свои компоненты:

  1. Это заставляет вас чувствовать себя более уверенно в своем коде.
  2. Тесты повышают вашу производительность.

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

В этой статье я расскажу вам обо всем, что вам нужно знать, чтобы написать тесты для ваших компонентов React. Я также расскажу о некоторых из лучших практик и техник, пока мы на нем. Давайте начнем!

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

Например, утверждение может быть чем-то простым: «Когда пользователь переходит к / login , должен быть представлен #login с id #login ». Итак, если окажется, что вы как-то испортили компонент входа в систему, утверждение вернет false. Утверждения не ограничиваются только тем, что визуализируется, вы также можете утверждать, как приложение реагирует на взаимодействия с пользователем и другие действия.

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

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

Модульные тесты помогают вам думать о каждом компоненте изолированно и рассматривать их как функции. Ваши модульные тесты для конкретного компонента должны ответить на следующие вопросы:

  1. Есть ли реквизиты? Если да, то что с ними делать?
  2. Какие компоненты он отображает?
  3. Должно ли оно иметь государство? Когда или как он должен обновлять состояние?
  4. Есть ли какая-то процедура, которой он должен следовать при монтировании, размонтировании или взаимодействии с пользователем?

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

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

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

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

Вам нужны определенные инструменты и зависимости, чтобы начать модульное и функциональное тестирование вашего приложения React. Я перечислил их ниже.

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

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

Даже если вы используете create-response-app, вам нужно будет установить этот пакет для рендеринга снимков. Тестирование снимков является частью библиотеки Jest. Таким образом, вместо рендеринга пользовательского интерфейса всего приложения вы можете использовать средство визуализации тестов для быстрой генерации сериализуемого вывода HTML из виртуального DOM. Вы можете установить его следующим образом:

1
yarn add react-test-renderer

response-dom / test-utils состоит из некоторых утилит тестирования, предоставленных командой React. Кроме того, вы можете использовать пакет Enzyme , выпущенный Airbnb. Фермент намного лучше, чем ReactTestUtils, потому что его легко утверждать, манипулировать и перебирать выходные данные ваших компонентов React. Мы начнем наши тесты с утилит React, а затем перейдем к Enzyme.

Чтобы установить Enzyme, выполните следующую команду.

1
yarn add enzyme enzyme-adapter-react-16

Добавьте код в src / SetupTests.js .

1
2
3
4
import { configure } from ‘enzyme’;
import Adapter from ‘enzyme-adapter-react-16’;
 
configure({ adapter: new Adapter() });

Более подробная информация об этом содержится в разделе « Компоненты тестирования » на странице «создать-реагировать-приложение».

Мы будем писать тесты для простого демонстрационного приложения, которое отображает основной / подробный вид списка продуктов. Вы можете найти демонстрационное приложение в нашем репозитории GitHub . Приложение состоит из компонента контейнера, известного как ProductContainer и трех компонентов представления: ProductList , ProductDetails и ProductHeader .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
.
├── package-lock.json
├── package.json
├── public
│ ├── index.html
│ └── manifest.json
├── src
│ ├── components
│ │ ├── App.js
│ │ ├── ProductContainer.js
│ │ ├── ProductDetails.jsx
│ │ ├── ProductHeader.js
│ │ ├── ProductList.jsx
│ ├── index.js
│ └── style.css

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

Загрузив демо, создайте каталог с именем __tests__   внутри / src / components / . Затем вы можете сохранить все тестовые файлы, связанные с этой функцией, в каталоге __tests__ . Тестеры обычно называют свои тестовые файлы как .spec.js или .test.js — например, ProductHeader.test.js или ProductHeader.spec.js .

Создайте файл ProductHeader.test.js, если вы этого еще не сделали. Вот как в основном будут выглядеть наши тесты:

01
02
03
04
05
06
07
08
09
10
describe(‘ProductHeader’, () => {
 
  it(‘passing test’, () => {
    expect(true).toBeTruthy();
  })
 
  it(‘failing test’, () => {
    expect(false).toBeTruthy();
  })
})

Набор тестов начинается с блока describe , который представляет собой глобальную функцию Jest, которая принимает два параметра. Первый параметр — это название набора тестов, а второй параметр — это фактическая реализация. Каждый it() в наборе тестов соответствует тесту или спецификации. Тест содержит одно или несколько ожиданий, которые проверяют состояние кода.

1
expects(true).toBeTruthy();

В Jest ожидание — это утверждение, которое либо возвращает true, либо false. Когда все утверждения в спецификации верны, говорят, что они прошли. В противном случае тест считается неудачным.

Например, мы создали две тестовые спецификации. Первый должен, очевидно, пройти, а второй потерпеть неудачу.

Примечание: toBeTruthy() является предопределенным сопоставителем. В Jest каждый сопоставитель делает сравнение между ожидаемым значением и фактическим значением и возвращает логическое значение. Доступно еще много соответствий, и мы рассмотрим их чуть позже.

create-реакции-приложение настроило все, что вам нужно для выполнения набора тестов. Все, что вам нужно сделать, это запустить следующую команду:

1
yarn test

Вы должны увидеть что-то вроде этого:

Тестовый вывод

Чтобы пройти неудачный тест, вы должны заменить toBeTruthy() на toBeFalsy() .

1
expects(false).toBeFalsy();

Это оно!

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

  • toBe();
  • toBeNull()
  • toBeDefined()
  • toBeUndefined()
  • toBeTruthy()
  • toBeFalsy()
  • toBeGreaterThan()
  • toBeLesserThan()
  • toMatch()
  • toContain()

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

Сначала мы ProductHeader пару тестов для компонента ProductHeader . Откройте файл ProductHeader.js, если вы этого еще не сделали.

01
02
03
04
05
06
07
08
09
10
import React, {Component} from ‘react’;
    
class ProductHeader extends Component {
    render() {
        return(
            <h2 className=»title»> Product Listing Page </h2>
        );
    }
};
export default ProductHeader;

Вам интересно узнать, почему я использовал здесь компонент класса вместо функционального компонента? Причина в том, что с ReactTestUtils сложнее тестировать функциональные компоненты. Если вам интересно узнать почему, у этого обсуждения Stack Overflow есть ответ.

Мы могли бы написать тест со следующими допущениями:

  1. Компонент должен отображать тег h2 .
  2. Тег h2 должен иметь класс с именем title .

Чтобы визуализировать компонент и получить соответствующие узлы DOM, нам нужен ReactTestUtils. Удалите фиктивные спецификации и добавьте следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
import React from ‘react’;
import ReactTestUtils from ‘react-dom/test-utils’;
import ProductsList from ‘../ProductsList’;
 
describe(‘ProductHeader Component’, () => {
 
    it(‘has an h2 tag’, () => {
     //Test here
    });
   
    it(‘is wrapped inside a title class’, () => {
     //Test here
    })
  })

Чтобы проверить существование узла h2 , нам сначала нужно будет отобразить наши элементы React в узле DOM в документе. Вы можете сделать это с помощью некоторых API, экспортируемых ReactTestUtils . Например, чтобы отобразить наш компонент <ProductHeader/> , вы можете сделать что-то вроде этого:

1
const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);

Затем вы можете извлечь тег h2 из компонента с помощью findRenderedDOMComponentWithTag('tag-name') . Он проверяет все дочерние узлы и находит узел, соответствующий имени tag-name .

Вот вся спецификация теста.

1
2
3
4
5
6
7
8
it(‘has an h2 tag’, () => {
 
     const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);
     var h2 = ReactTestUtils.findRenderedDOMComponentWithTag(
      component, ‘h2’
    );
    
 });

Попробуйте сохранить его, и ваш тестовый бегун должен показать вам, что тест пройден. Это несколько удивительно, потому что у нас нет оператора expect() как в нашем предыдущем примере. Большинство методов, экспортируемых ReactTestUtils, имеют встроенные ожидания. В этом конкретном случае, если утилита тестирования не может найти тег h2 , она выдаст ошибку, и тесты автоматически пройдут неудачу.

Теперь попробуйте создать код для второго теста. Вы можете использовать findRenderedDOMcomponentWithClass() чтобы проверить, есть ли какой-либо узел с классом ‘title’.

1
2
3
4
5
6
7
it(‘has a title class’, () => {
 
     const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);
     var node = ReactTestUtils.findRenderedDOMComponentWithClass(
      component, ‘title’
    );
   })

Это оно! Если все идет хорошо, вы должны увидеть результаты в зеленом цвете.

Результаты теста

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

Если в какой-то момент вы застряли или нуждаетесь в помощи, сообщите нам об этом в комментариях.