Тестирование компонентов React с использованием нового API контекста React
Когда я пишу это, React 16.3 только что приземлился вчера. Вместе с ним идет новый стабильный контекстный API. Его легко использовать, и есть много отличных статей, которые покажут вам, как это сделать. Это невероятно крутое дополнение к React. Это делает компоненты более декларативными, упрощает ваш код, не требуя, чтобы компоненты имели дополнительные свойства, просто чтобы передать их дочернему компоненту.
Я работал над второстепенным проектом, в котором контекстный API был именно тем, что мне было нужно, поэтому я пошел дальше и переделал свое приложение для его использования. Он был таким замечательным и простым в использовании, как я себе представлял, жизнь была прекрасна! Была одна проблема… тестирование компонентов стало затруднительным.
В этой статье я покажу вам свое решение для компонентов модульного тестирования, использующих новый контекстный API. В частности, так я тестирую компонент, который завернут в Provider
компонент.
const AddToCartButton = () => { return ( <Provider.Consumer> { context => { // test what's in here }} </Provider.Consumer> ) }
В этом примере я буду использовать Jest в качестве среды тестирования с помощью Enzyme для работы с компонентами React.
Компонент, который мы хотим протестировать, будет выглядеть примерно так
// AddToCartButton.jsx import React from 'react'; import { MyContext } from './Provider'; const AddToCartButton = () => { return ( <MyContext.Consumer> {context => { const { onAddToCartClick } = context; return ( <button onClick={onAddToCartClick}> Add to Cart </button> ); }} </MyContext.Consumer> ); }; export default AddToCardButton;
Мы могли бы написать простой тест: когда нажимается кнопка, вызывается onAddToCartClick
. Первое, что нам нужно сделать, это смоделировать MyContext
, используя ручные макеты Jest. В каталоге, содержащем файл Provider, вам нужно создать еще один каталог с именем __mocks__
. Если вы храните компоненты в каталоге components
, ваша файловая структура будет выглядеть примерно так
├── components │ ├── Provider.jsx │ ├── __mocks__ │ │ └── Provider.jsx
Макет для MyContext
на самом деле будет довольно простым, но сначала нам нужно создать функцию mock
для onAddToCartClick
, чтобы в нашем тесте мы утверждали, что она была вызвана.
Создайте еще один файл с именем testHelpers.js
(этот файл можно называть как угодно). В этом файле вы хотите создать объект контекста со свойством, которое является фиктивной функцией.
// testHelpers.js const context = { onAddToCartClick: jest.fn() } export default context
Отлично, теперь в __mocks__/Provider.jsx
давайте импортируем этот объект и передадим его компоненту для использования в тесте.
// __mocks__/Provider.jsx import context from '../testHelpers'; export const MyContext = ({ Consumer(props) { return props.children(context) } })
Довольно просто, правда? Это создает объект с именем MyContext
, который использует синтаксис определения метода ES6 для создания свойства с именем Consumer
, где значением является функция. Функция принимает объект с именем props
и предполагает, что props
является объектом, у которого есть свойство children
. Также предполагается, что свойство children
является функцией. Затем внутри функции мы вызываем props.children
и передаем ему наш объект контекста, содержащий нашу фиктивную функцию.
Теперь мы можем написать наш тест. Мы имитируем MyContext
, имитируем щелчок по нашей кнопке и подтвердим, что наша фиктивная функция была вызвана.
// AddToCartButton.test.js import React from 'react'; import { mount } from 'enzyme'; import AddToCartButton from './AddToCartButton'; import context from './testHelper'; jest.mock('./Provider'); test('function called on click', () => { const component = mount(<AddToCartButton />); component.find('button').simulate('click'); expect(context.onAddToCartClick.mock.calls.length).toBe(1); });
Вот и все. Я надеюсь, что вы найдете это полезным, и, пожалуйста, дайте мне знать, как это происходит!