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

Прежде всего, что такое Jest и Enzyme? Jest был создан Facebook и представляет собой фреймворк для тестирования кода JavaScript и React. Вместе с Enzyme от Airbnb, утилитой для тестирования, она идеально подходит для простого тестирования вашего приложения React.

Снимки на помощь
Давайте начнем с простого тестирования простого компонента без сохранения состояния (он же тупой), который отображает простой элемент ссылки, содержащий заголовок и URL.

import React from 'react';
import { string } from 'prop-types';
const Link = ({ title, url }) => <a href={url}>{title}</a>;
Link.propTypes = {
  title: string.isRequired,
  url: string.isRequired
};
export default Link;

Мы хотим протестировать этот компонент, проверив, правильно ли отображаются свойства и правильно ли он отображается. С Jest у нас есть очень простой способ сделать это, создав снимки состояния. При первом запуске теста создается снимок. Затем вы можете просмотреть созданный файл, чтобы проверить, соответствует ли этот визуализированный компонент желаемому результату. Напишем первый тест для нашего компонента. В этом случае мы будем использовать поверхностный рендерер Enzyme, чтобы помочь нам создать снимок.

import React from 'react';
import { shallow } from 'enzyme';
import { shallowToJson } from 'enzyme-to-json';
import Link from './Link';
describe('Link', () => {
  it('should render correctly', () => {
    const output = shallow(
      <Link title="mockTitle" url="mockUrl" />
    );
    expect(shallowToJson(output)).toMatchSnapshot();
  });
});

После запуска тестов Jest автоматически создаст каталог __snapshots__ и файл моментального снимка, содержащий результат теста. В этом случае он создает файл Link-spec.js.snap, содержащий следующий результат рендеринга нашего компонента.

// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Link should render correctly 1`] = `
<a
  href="mockUrl"
>
  mockTitle
</a>
`;

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

import React, { Component } from 'react';
import { string } from 'prop-types';
class Link extends Component {
  handleClick() => {
    alert('clicked!');
  };
  render() {
    const { title, url } = this.props;
    return <a href={url} onClick={this.handleClick}>{title}</a>;
  }
}

Когда вы запустите наш предыдущий тест в этот момент, он завершится ошибкой, поскольку ваш сохраненный снимок не соответствует новому снимку, потому что мы добавили свойство onClick. Если снимок выглядит правильно, вы можете легко обновить его, нажав u. Теперь снимок будет выглядеть так:

exports[`Link should render correctly 1`] = `
<a
  href="mockUrl"
  onClick={[Function]}
  target=""
>
  mockTitle
</a>
`;

Мы собираемся создать второй тестовый пример, чтобы проверить, правильно ли обрабатывается событие onClick. Наши события щелчка вызывают предупреждение, и мы можем легко смоделировать эту функцию в Jest, используя jest.fn (). После поверхностного рендеринга нашего компонента мы имитируем событие щелчка и проверяем, вызывается ли предупреждение с правильной строкой. (В этом случае файл снимка не создается, поскольку мы не используем функцию снимка)

it('should handle the click event', () => {
  window.alert = jest.fn();
  const output = shallow(
    <Link title="mockTitle" url="mockUrl" />
  );
  output.simulate('click');
  expect(window.alert).toHaveBeenCalledWith('clicked');
});

Проверка состояния
Мы также можем легко проверить состояние нашего компонента. Обновите компонент, инициировав состояние в конструкторе и развернув событие щелчка с помощью функции setState. При нажатии на элемент мы теперь также собираемся обновить состояние clicked с false на true.

class Link extends Component {
  constructor(props) {
    super(props);
    this.state = { clicked: false };
  }
  handleClick = () => {
    alert('clicked');
    this.setState({ clicked: true });
  }
  ...
}

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

it('should handle state changes', () => {
  const output = shallow(
    <Link title="mockTitle" url="mockUrl" />
  );
  
  expect(output.state().clicked).toEqual(false);
  output.simulate('click');
  expect(output.state().clicked).toEqual(true);
});

Искусство издевательства
Одно из лучших преимуществ Jest - легкость создания всевозможных имитов. Мы уже использовали jest.fn () в одном из наших модульных тестов, который представляет собой очень простую фиктивную функцию, возвращающую шпиона, но вы также можете имитировать целые файлы.

Если вы не издеваетесь, значит, вы не проводите модульное тестирование!

Самый простой способ имитировать файлы - использовать функцию jest.mock, которая автоматически имитирует файл, возвращая имитированные функции. Давайте попробуем поиздеваться над react-dom и проверим, была ли вызвана функция рендеринга.

import React from 'react';
import { render } from 'react-dom';
import Link from './Link';
jest.mock('react-dom');
describe('Link', () => {
  it('should render correctly', () => {
    expect(render).toHaveBeenCalledWith(
      <Link title="mockTitle" url="mockUrl" />, 'element-node'
    );
    expect(render).toHaveBeenCalledTimes(1);
  });
});

Мы создаем тест, как обычно, но добавляем строку jest.mock. Теперь функция render в react-dom - это фиктивная функция, которая возвращает шпион, которого мы теперь можем использовать в нашем тесте, чтобы проверить, был ли он вызван с правильными свойствами.

Если вы не хотите, чтобы Jest автоматически создавал фиктивные функции вашего файла, вы можете легко создать фиктивный файл самостоятельно. Просто создайте каталог __mocks_ _ и добавьте туда файл для фиксации. Теперь Jest будет использовать этот файл при вызове jest.mock в модульном тесте. Для примера response-dom создайте файл response-dom.js, который возвращает шпионскую функцию рендеринга.

export default {
  render: jest.fn(),
};

В нашем примере мы возвращаем шпионскую функцию, на которой мы можем проверить, правильно ли она вызывается, но вы шпион также можете возвращать значения или реализации.

render: jest.fn().mockReturnValue('component is rendered'),
render: jest.fn().mockImplementation(() => 'mock implementation'),

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

Нет причин не начинать тестирование сегодня;)

Спасибо, что нашли время прочитать эту историю! Если вам понравилась эта история, нажмите 👏🏻 ниже, чтобы другие люди увидели ее здесь, на Medium.

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