Мир тестирования в React, мягко говоря, огромен. Существует не только ряд инструментов для использования, но и различные типы методологий тестирования. Jest — отличный инструмент для запуска тестов, потому что он поставляется в комплекте с приложением Create React и разработан Facebook, командой разработчиков React. Мы также будем использовать Enzyme в качестве нашей библиотеки утверждений, созданной командой AirBnb.

В этой статье предполагается, что вы будете использовать приложение Create React, что позволит нам начать тестирование с Jest без особой настройки, а Enzyme можно импортировать как обычную зависимость (с небольшой настройкой, которую мы рассмотрим)

Модульное тестирование

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

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

Настройка Jest

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

Настройка фермента

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

npm install --save-dev enzyme enzyme-adapter-react-16

Здорово! Остался последний шаг, который нам нужно сделать, чтобы начать использовать Enzyme в нашем проекте. В папке src создайте новый файл с именем setupTests.js и в нем введите следующее:

import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
configure({ adapter: new Adapter() });

Теперь в вашем терминале в каталоге вашего проекта запустите команду npm test, чтобы запустить средство запуска тестов Jest.

Разработка через тестирование (TDD)

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

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

Документация по коду

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

Давайте начнем

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

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

Итак, у нас есть 2 основных компонента, наш product-item и наш cart. Внутри них находятся другие компоненты, у каждого из которых есть задание . Это еще один отличный способ подумать о том, что можно тестировать. Каждый элемент с синим квадратом имеет одну конкретную функцию, которую мы можем надежно протестировать, будь то отображение правильной информации о продукте или правильный расчет корзины. Это гарантирует тестовый случай.

Опишите свои тесты

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

Помните,в TDD мы пишем тесты до того, как напишем код. Итак, первое, что мы хотим сделать, это создать папку components в нашей папке src и создать папку для каждого компонента. Тогда мы назовем product-item и cart.

Начнем с product-item .

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

Давайте начнем описывать наш тестовый пример компонента

product-item.test.js

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

Пишем наши первые тесты

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

Итак, первое, что нам нужно сделать, это импортировать пару новых модулей в наш тестовый файл. В верхней части файла product-item.test.js импортируйте следующее

import { shallow } from 'enzyme';
import ProductItem from './product-item';

shallow — это API от энзима, который позволяет нам поверхностно визуализировать наши компоненты для тестирования. shallow отлично подходит для модульных тестов, потому что он отображает только тестируемый компонент и не отображает какие-либо дочерние компоненты внутри него. Позже мы поговорим о mount, который отображает все дерево целиком и идеально подходит для интеграционных тестов.

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

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

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

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

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

Идите вперед и создайте файл с именем product-item.js в том же каталоге, что и ваш тест, и создайте стандартный компонент React.

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

Это ваш первый настоящий проваленный тест. Это точное сообщение об ошибке, которое вы увидите, если название вашего продукта по какой-то причине не отобразится. Как вы можете видеть в нашем утверждении, мы ищем элемент с классом .product-title. Стоит отметить, что вы можете использовать разные селекторы, отличные от класса. Вы также можете настроить таргетинг через ID или, что более полезно в реальном приложении, с помощью компонента JSX.

Давайте исправим это. Перейдите к вашему файлу product-item и давайте добавим элемент с классом product-title.

ааааааааааааааааааааааааааааааааа Валла!

Все наши тесты проходят!

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

Итак, здесь происходит несколько новых вещей. Во-первых, мы моделируем некоторые данные для отправки в наш компонент. Это часто называют организацией при тестировании. Это означает, что мы собираем некоторые данные для использования в нашем тесте. Далее мы фактически передаем этот объект productData в наш неглубоко визуализированный компонент.

Так что здесь вы можете увидеть, как тестирование начинает немного сиять. На самом деле мы можем найти наше поле ввода (которого, опять же, еще не существует) и имитировать пользователя, вводящего в него значение 10. Это часто упоминается как acting в тестировании.

Когда мы сейчас проверим наш тестовый бегун, мы увидим новый неудачный тест:

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

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

Хорошо, эта ошибка исчезла, но теперь у нас есть новая.

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

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

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

Одна новая вещь здесь. Теперь мы используем .text() для проверки текста внутри нашего блока сообщений об ошибках. Также ничего не стоит то, что этот тест действительно делает 2 вещи. Это также не удастся, если он не сможет найти сообщение об ошибке. Как вам эффективность? Давайте теперь посмотрим на компонент.

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

Оптимизация ваших тестов

Я думаю, что это хорошая возможность представить пару новых концепций, чтобы сделать наш тест немного менее подробным. Глядя на наш блок описания для Product Form, мы видим, что в нем есть некоторая избыточность. Первые 3 строки этого теста точно такие же. Мы упорядочиваем наши данные, поверхностно визуализируем наш элемент с этими данными и имитируем пользовательский ввод. У Enzyme есть хороший помощник под названием beforeEach, который позволяет нам выполнять некоторый код перед каждым тестом в наборе тестов. Давайте переместим эти 3 строки в метод beforeEach.

Отлично, так намного чище.

Интеграционное тестирование

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

Процесс написания интеграционного теста начнется почти так же, как и наш модульный тест. Итак, мы начнем с описания нашего взаимодействия в App.test.js

Итак, пара новинок. Теперь мы используем mount, потому что хотим, чтобы наши дочерние компоненты App.js (корзина и товар) отображались, чтобы мы могли проверить их значения DOM.

Давайте напишем код в App.js, чтобы пройти этот тест.

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

Вывод

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

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