Тестирование компонентов 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);
});

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