Когда я вырос как программист, я понял, как важно писать чистый и читаемый код. Когда что-то неизбежно идет не так и отладка останавливает ваш прогресс, наличие кода, за которым вы легко можете следовать, имеет большое значение для минимизации разочарования от обнаружения того, что не так. Однако иногда легче сказать, чем сделать, написать чистый код. Здесь может пригодиться тестирование, и, к счастью, философия разработки через тестирование (TDD) может помочь вам не сбиться с пути и в конечном итоге облегчить вам жизнь.

Вкратце объясним, что TDD - это процесс написания тестовых примеров для ожидаемых результатов до написания кода. Это, по сути, заставит вас написать код для этих ожидаемых результатов, потенциально минимизируя ошибки в процессе.

Также есть несколько типов тестов:

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

Сегодня я сосредоточусь на модульном тестировании Front-end React, поскольку оно может показаться немного более абстрактным для концептуализации и реализации. Я также остановлюсь на двух наиболее популярных инструментах для внешнего тестирования: Enzyme и Jest. Мы используем несколько простых компонентов React в качестве примеров, чтобы кратко рассмотреть, как эти инструменты работают как по отдельности, так и в унисон друг с другом, и мы напишем несколько модульных тестов, чтобы увидеть, как они выглядят. Во-первых, это поможет понять, что представляет собой каждый из этих инструментов.

Фермент

Enzyme, согласно его документации, является утилитой для тестирования Javascript. Он разработан для простоты утверждения, манипулирования и обхода вывода вашего компонента React. По сути, это библиотека, которая, помимо прочего, обертывает собственный React TestUtils от React, чтобы упростить написание модульных тестов для вашего приложения. Если вы используете Node, вы можете просто установить фермент из командной строки:

npm install --save-dev enzyme

Настроить с помощью Mocha и Chai

Однако сам по себе он не является фреймворком для модульного тестирования. В этом случае Enzyme, как правило, требует наличия нескольких других фреймворков, чтобы в полной мере использовать свои возможности. Часто вы увидите популярные внешние средства запуска тестов (например, mocha) и библиотеки утверждений (например, chai), включенные в проект. Кроме того, с самым последним обновлением Enzyme 3 вам потребуется установить энзим-адаптер, чтобы настроить Enzyme для работы с той версией React, которую вы используете в данный момент:

npm install --save-dev mocha chai enzyme-adapter-react-<version-of-react-you're-running>

После того, как вы установили все компоненты, вам необходимо настроить Enzyme для работы с вашим проектом, создав файл конфигурации со следующим:

import { expect } from 'chai';
import { mount, render, shallow, configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-<version-of-react-you're-running>';
configure({ adapter: new Adapter() });
global.mount = mount; // if you're using mount
global.render = render; // if you're using render
global.shallow = shallow; // the more generally used and typical enzyme function for unit testing

Проведение ферментных тестов

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

import React from 'react';
import { expect } from 'chai';
import { shallow } from 'enzyme';
import Hello from './Hello';
describe('Hello', () => {
  it('Says hello to the visitor', () => {
    let name = 'Jon';
    const greeting = shallow(<Hello name={name} />);
    expect(greeting.find('p').text()).to.equal('Hello, Jon!');
  });
});

Функция описать используется, по сути, для обозначения заголовка вашего набора тестов, тогда как функция it определяет индивидуальный тесты в вашем наборе. Функция expect, предоставляемая chai, определяет результат, который тест ожидает увидеть от вашего кода. В функции expect мы видим, что Enzyme использует таргетинг, подобный jQuery, для точного определения тегов paragraph (p) внутри компонента. Мы можем использовать функцию text, чтобы проверить, какой текст содержится в тегах p, и сравнить его с тегом to.equal ожидаемый результат функции.

Функция shallow в Enzyme также очень удобна, поскольку позволяет выполнять неглубокую визуализацию любого компонента, который вы ей передаете. Однако обратите внимание, что если бы наш компонент Hello имел какие-либо вложенные компоненты внутри него, то эти компоненты НЕ были бы визуализированы с использованием shallow. Именно здесь вступают в игру функции mount и render, которые мы настроили ранее.

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

import React from 'react';
const Hello = ({ name }) => {
    return (
        <p>Hello, {name}!</p>
    )
}
export default Hello;

Чудесно! Как видите, Enzyme - полезная утилита для тестирования, несмотря на то, что она требует большого количества внешних модулей. Один из способов уменьшить количество лишних слов, связанных с использованием Enzyme, - это заменить мокко и чай одной удобной структурой - Jest.

Шутовое тестирование

Jest описывается как среда модульного тестирования для тестирования сервисов и приложений React. Jest - довольно простая в реализации структура. На самом деле настолько просто, что если вы инициализируете проект React с помощью create-react-app:

create-react-app my-super-awesome-app

тогда ваш проект уже будет предустановлен и сможет использовать Jest. Его основная цель - определение тестов, создание утверждений и создание имитаторов, что делает его универсальным решением для работы с Enzyme. Однако вы также можете запускать определенные типы тестов только с Jest:

import React from 'react';
import renderer from 'react-test-renderer';

import Hello from './Hello';

describe('Hello Snapshot', () => {
  test('renders', () => {
    const component = renderer.create(
      <Hello name={'Jon'}/>
    );
    let tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });
});

Обратите внимание, как Jest использует функцию описать, чтобы дать общее описание набора тестов, при определении отдельных тестов с помощью test функция.

Описание снимков Jest

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

ShallowWrapper {
  "length": 1,
  Symbol(enzyme.__root__): [Circular],
  Symbol(enzyme.__unrendered__): <Hello
    name="Jon"
/>,
  Symbol(enzyme.__renderer__): Object {
    "batchedUpdates": [Function],
    "getNode": [Function],
    "render": [Function],
    "simulateEvent": [Function],
    "unmount": [Function],
  },
  Symbol(enzyme.__node__): Object {
    "instance": null,
    "key": undefined,
    "nodeType": "host",
    "props": Object {
      "children": Array [
        "Hello, ",
        "Jon",
        "!",
      ],
    },
    "ref": null,
    "rendered": Array [
      "Hello, ",
      "Jon",
      "!",
    ],
    "type": "p",
  },
  Symbol(enzyme.__nodes__): Array [
    Object {
      "instance": null,
      "key": undefined,
      "nodeType": "host",
      "props": Object {
        "children": Array [
          "Hello, ",
          "Jon",
          "!",
        ],
      },
      "ref": null,
      "rendered": Array [
        "Hello, ",
        "Jon",
        "!",
      ],
      "type": "p",
    },
  ],
  Symbol(enzyme.__options__): Object {
    "adapter": ReactSixteenAdapter {
      "options": Object {
        "enableComponentDidUpdateOnSetState": true,
      },
    },
  },
}

Сохраняя этот снимок, вы можете запускать будущие снимки с сохраненным, чтобы увидеть, были ли какие-либо изменения в DOM. Если что-то не так, тест завершится неудачно, что означает либо ошибку в коде, либо изменение базы кода и необходимость обновления снимка. Если последнее верно, вы можете легко обновить свой снимок из консоли, где отображается ваш неудавшийся тест, нажав клавишу «u». Это заменит старый снимок новым, что позволит вам сравнивать будущие результаты снимка с новыми результатами вашего измененного кода.

Фермент и шутка

Теперь, когда мы увидели, что они работают по отдельности, давайте посмотрим, как Jest и Enzyme могут использоваться вместе.

import React from 'react';
import { shallow } from 'enzyme';
import Hello from './Hello';
describe('Hello', () => {
  it('Says hello to the visitor', () => {
    let name = 'Jon';
    const greeting = shallow(<Hello name={name} />);
    expect(greeting.find('p').text()).toEqual('Hello, Jon!');
  });
});
describe('Hello Snapshot', () => {
  it('renders', () => {
    const component = shallow(<Hello name={'Jon'}/>);
    expect(component).toMatchSnapshot();
  });
});

Как видите, совместное использование Jest и Enzyme снижает беспорядок, который требуется каждому при работе по отдельности. Благодаря Jest нам больше не нужно устанавливать chai или требовать в expect. Кроме того, при тестировании снимка нашего компонента нам больше не нужно добавлять строку, в которой мы преобразовали дерево DOM в JSON. Эти два языка прекрасно сочетаются друг с другом, и их совместное использование может облегчить тестовую нагрузку в целом. С помощью этого теста мы могли бы написать тот же компонент, что и раньше:

import React from 'react';
const Hello = ({ name }) => {
    return (
        <p>Hello, {name}!</p>
    )
}
export default Hello;

И все же пройти все наши тесты и получить компонент, который работает так, как мы ожидаем.

Резюме

В этом посте я только начал поверхностно рассматривать возможности фронтенд-тестирования с помощью Jest и Enzyme. Я считаю, что с помощью нашего простого компонента Hello я смог показать, как работают Jest и Enzyme, немного лучше. Я также надеюсь, что мне удалось сместить фокус с сравнения этих двух и вместо этого рассматривать их как инструменты, которые можно использовать вместе. В будущем я надеюсь глубже погрузиться в более сложные ситуации, тем самым продемонстрировав истинную силу одновременного использования TDD, Jest и Enzyme.