Практическое руководство с живыми примерами

Было ли у вас когда-нибудь ощущение, что код тестирования вашего реагирующего компонента просто тестирует UI-фреймворк и связь компонентов, но он не настолько близок к тому, чтобы имитировать реальное поведение пользователя. Если вы согласны со мной, читайте дальше, чтобы познакомиться с другим способом тестирования компонентов с использованием библиотеки под названием react-testing-library.

Библиотека была задумана замечательным Кентом С. Доддсом, и мне нравится руководящий принцип ее создания:

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

Давайте узнаем, как библиотека тестирования помогает нам писать модульные / интеграционные тесты, на примере простого приложения Todo.

Вы можете играть с приложением на codeandbox.io. Все фрагменты кода, обсуждаемые в посте, доступны в этом примере репо.

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

У нас есть TodoApp компонент-контейнер, который имеет TodoList, который представляет собой набор всех существующих задач, каждый из которых представлен TodoListItem. У каждого элемента TodoList справа есть кнопка удаления, а слева - кнопка отметки как выполненная. Компонент TodoForm внизу позволяет пользователю добавлять новые элементы в TodoList.

Совет: используйте Бит, чтобы повторно использовать компоненты и быстрее создавать! Легко систематизируйте и используйте свои компоненты, чтобы создавать больше приложений вместе со своей командой. Попробуйте.



Написание нашего первого теста

Давайте возьмем самый простой из них, компонент TodoListItem, и добавим к нему тестовое покрытие.

Код реализации для TodoListItem приведен выше, чтобы вы могли легче понять его при просмотре приведенного ниже тестового кода.

TodoListItem принимает 3 свойства:

  • item: который является фактическим элементом todo и имеет три атрибута: index, value и done
  • markTodoDone функция, которая будет вызываться, когда задача будет помечена как завершенная
  • removeItem функция, которая будет вызываться при удалении задачи

Давайте начнем анализировать тестовый код, чтобы понять, что там происходит:

Первым шагом будет фактический рендеринг компонента, и вот что делает эта строка:

const {getByTestId} = render(<TodoListItem item={item} index=
                           {itemIndex} markTodoDone={markTodoDone}               
                           removeItem={removeItem}/>)

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

В нашем случае мы используем один из таких помощников под названием getByTestId, который, по сути, является ярлыком для [data-testid=<elementId>]. Если вы проверите код нашего компонента, вы увидите, что данные-testid добавляются к различным элементам для каждого тестирования.

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

  • getByLabelText: поиск метки, соответствующей заданному тексту.
  • getByPlaceholderText: поиск элементов по их атрибуту-заполнителю.
  • getByText: поиск элементов с текстовым содержанием.

Библиотека response-testing-library использует dom-testing-library (того же автора) за кулисами, чтобы предложить служебные функции тестирования DOM для облегчения выполнения запросов. Следует отметить, что для каждого из указанных выше get помощников существует соответствующий getAll API, который возвращает все элементы, а не только первый, и _16 _ / _ 17_, которые возвращают _18 _ / _ 19_ вместо выдачи ошибки.

Вы видите, что в нашем тесте мы используем помощник getByTestId, чтобы убедиться, что TodoListItem правильно отображает item.value.

События стрельбы

Чтобы запускать такие события, как click или change, мы должны использовать один из перечисленных выше селекторов для извлечения элемента, а затем использовать помощник fireEvent для имитации взаимодействия пользователя с компонентом.

В нашем примере этот фрагмент кода делает именно это…

fireEvent.click(getByTestId('markAsCompleted'))
expect(markTodoDone).toBeCalledWith(itemIndex)

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

Тестирование взаимодействия с формой

Давайте рассмотрим TodoFormTest.js код, чтобы понять, как тестировать элементы формы.

Опять же, код компонента указан выше для удобства. Тест на то же самое ниже:

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

В этом тесте мы утверждаем, что функция обратного вызова jest mock вызывается вместе со значением, введенным в элемент ввода.

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

Пожалуйста, просмотрите другие примеры в репозитории, чтобы узнать больше о том, что может делать react-testing-library. Не стесняйтесь комментировать и спрашивать меня о чем угодно!

Учить больше