Статьи

Руководство по тестированию компонентов React

React — это фреймворк, который достиг прогресса в сообществе разработчиков JavaScript. React имеет мощную композиционную среду для разработки компонентов. Компоненты React — это куски кода многократного использования, которые вы можете использовать в своем веб-приложении.

Компоненты React тесно не связаны с DOM, но насколько легко их тестировать? В этом примере давайте рассмотрим, что нужно для модульного тестирования компонентов React. Я покажу, как сделать ваши компоненты тестируемыми.

Имейте в виду, я говорю только о модульных тестах , которые являются особым видом теста. (Для получения дополнительной информации о различных видах тестов я рекомендую вам прочитать « Тестирование JavaScript: тестирование модулей по функциональным и интеграционным тестам ».)

В модульных тестах меня интересуют две вещи: быстрая и разрушительная обратная связь. Благодаря этому я могу выполнять изменения с высокой степенью достоверности и качеством кода. Это дает вам определенный уровень уверенности в том, что ваши компоненты React не окажутся мертвыми в браузере. Способность быстро получать хорошие отзывы дает вам конкурентное преимущество, которое вы захотите сохранить в современном мире гибкой разработки программного обеспечения.

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

Если вы скачаете и запустите демонстрационный пример кода, вы увидите страницу, подобную этой:

Великая демо обезьяна в компонентах реагирования

Написать тестируемые компоненты

В React хорошим подходом является начать с иерархии компонентов. Принцип единой ответственности приходит на ум при создании каждого отдельного компонента. Реагирующие компоненты используют композицию объектов и отношения.

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

FilterableGreatApeList
|_ GreatApeSearchBar
|_ GreatApeList
   |_ GreatApeRow

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

В компонентах React избегайте использования наследования для создания повторно используемых компонентов. Если вы пришли из классического объектно-ориентированного программирования, имейте это в виду. Компоненты React не знают своих детей раньше времени. Тестирование компонентов, которые происходят от длинной цепочки предков, может быть кошмаром.

Я позволю вам изучить FilterableGreatApeList самостоятельно. Это компонент React с двумя отдельными компонентами, которые здесь интересны. Не стесняйтесь изучать юнит-тесты, которые идут с ним тоже.

Например, чтобы создать тестируемый GreatApeSearchBar

 class GreatApeSearchBar extends Component {
  constructor(props) {
    super(props);

    this.handleShowExtantOnlyChange = this.handleShowExtantOnlyChange.bind(this);
  }

  handleShowExtantOnlyChange(e) {
    this.props.onShowExtantOnlyInput(e.target.checked);
  }

  render() {
    return(
      <form>
        <input
          id="GreatApeSearchBar-showExtantOnly"
          type="checkbox"
          checked={this.props.showExtantOnly}
          onChange={this.handleShowExtantOnlyChange}
        />

        <label htmlFor="GreatApeSearchBar-showExtantOnly">Only show extant species</label>
      </form>
    );
  }
}

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

Обратите внимание, что с React тестируемые компоненты поставляются бесплатно, прямо из коробки. Здесь нет ничего особенного — обработчик событий, JSX и метод рендеринга.

Следующим компонентом React в иерархии является GreatApeList

 class GreatApeList extends Component {
  render() {
    let rows = [];

    this.props.apes.forEach((ape) => {
      if (!this.props.showExtantOnly) {
        rows.push(<GreatApeRow key={ape.name} ape={ape} />);

        return;
      }

      if (ape.isExtant) {
        rows.push(<GreatApeRow key={ape.name} ape={ape} />);
      }
    });

    return (
      <div>
        {rows}
      </div>
    );
  }
}

Это компонент React, который имеет компонент GreatApeRow Это самая мощная композиционная модель React на работе. Обратите внимание на отсутствие наследования при создании повторно используемых, но тестируемых компонентов.

В программировании композиция объектов — это шаблон проектирования, который включает элементы, управляемые данными. Чтобы думать об этом иначе, GreatApeListGreatApeRow Именно эти отношения между компонентами пользовательского интерфейса определяют дизайн. В компоненты React встроен этот образ мышления. Такой взгляд на элементы пользовательского интерфейса позволяет вам написать несколько хороших модульных тестов.

Здесь вы проверяете флаг this.props.showExtantOnly Это свойство showExtantOnlyGreatApeSearchBar

Для модульных тестов, как вы проводите модульное тестирование компонентов React, которые зависят от других компонентов? Как насчет компонентов, переплетенных друг с другом? Это отличные вопросы, о которых нужно помнить, поскольку мы скоро приступим к тестированию. У компонентов реагирования могут быть секреты, которые можно открыть.

А пока давайте посмотрим на GreatApeRow

 class GreatApeRow extends Component {
  render() {
    return (
      <div>
        <img
          className="GreatApeRow-image"
          src={this.props.ape.image}
          alt={this.props.ape.name}
        />

        <p className="GreatApeRow-detail">
          <b>Species:</b> {this.props.ape.name}
        </p>

        <p className="GreatApeRow-detail">
          <b>Age:</b> {this.props.ape.age}
        </p>
      </div>
    );
  }
}

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

Тестовые утилиты

Давайте вспомним нашу самую большую проблему, когда речь заходит о тестировании компонентов React. Как выполнить модульное тестирование отдельного компонента в отдельности? Ну, как оказалось, есть изящная утилита, которая позволяет вам это сделать.

Shallow Renderer в React позволяет визуализировать компонент на один уровень глубиной. Отсюда вы можете утверждать факты о том, что делает метод рендеринга. Что примечательно, так это то, что для этого не требуется DOM.

Используя ES6, вы используете его так:

 import ShallowRenderer from 'react-test-renderer/shallow';

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

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

Остается только один вопрос: как далеко эта маленькая утилита может увести нас с компонентами React?

Положил все это вместе

Взгляните, например, на GreatApeList Какую главную проблему вы пытаетесь решить? Этот компонент показывает вам список отличных обезьян на основе фильтра.

Эффективным модульным тестом является передача списка и проверка фактов о том, что делает этот компонент React. Мы хотим, чтобы он фильтровал великих обезьян на основе флага.

Один из подходов состоит в том, чтобы сделать это:

 import GreatApeList from './GreatApeList';

const APES = [{ name: 'Australopithecus afarensis', isExtant: false },
  { name: 'Orangutan', isExtant: true }];

// Arrange
const renderer = new ShallowRenderer();
renderer.render(<GreatApeList
  apes={APES}
  showExtantOnly={true} />);

// Act
const component = renderer.getRenderOutput();
const rows = component.props.children;

// Assert
expect(rows.length).toBe(1);

Обратите внимание, что я тестирую компоненты React с использованием Jest. Подробнее об этом см. « Как тестировать компоненты React с помощью Jest ».

В JSX взгляните на showExtantOnly={true} Синтаксис JSX позволяет вам установить состояние ваших компонентов React. Это открывает множество способов для модульного тестирования компонентов в определенном состоянии. JSX понимает основные типы JavaScript, поэтому true

Со списком в стороне, как насчет GreatApeSearchBar Он имеет этот обработчик события в свойстве onChange

Один хороший юнит-тест должен сделать это:

 import GreatApeSearchBar from './GreatApeSearchBar';

// Arrange
let showExtantOnly = false;
const onChange = (e) => { showExtantOnly = e };

const renderer = new ShallowRenderer();
renderer.render(<GreatApeSearchBar
  showExtantOnly={true}
  onShowExtantOnlyInput={onChange} />);

// Act
const component = renderer.getRenderOutput();
const checkbox = component.props.children[0];

checkbox.props.onChange({ target: { checked: true } });

// Assert
expect(showExtantOnly).toBe(true);

Для обработки и тестирования событий вы используете один и тот же метод визуализации. Метод getRenderOutput Здесь onShowExtantOnlyInputonChange

На более тривиальном модульном тесте, как насчет компонента GreatApeRow Он отображает отличную информацию об обезьяне с использованием HTML-тегов. Оказывается, вы можете использовать мелкий рендер для проверки этого компонента.

Например, давайте удостоверимся, что мы визуализируем изображение:

 import GreatApeRow from './GreatApeRow';

const APE = {
  image: 'https://en.wikipedia.org/wiki/File:Australopithecus_afarensis.JPG',
  name: 'Australopithecus afarensis'
};

// Arrange
const renderer = new ShallowRenderer();
renderer.render(<GreatApeRow ape={APE} />);

// Act
const component = renderer.getRenderOutput();
const apeImage = component.props.children[0];

// Assert
expect(apeImage).toBeDefined();
expect(apeImage.props.src).toBe(APE.image);
expect(apeImage.props.alt).toBe(APE.name);

С компонентами React все это сосредоточено вокруг метода render Это делает несколько интуитивно понятным, чтобы точно знать, что вам нужно проверить. Благодаря мелкому рендереру вы можете лазерно сфокусироваться на одном компоненте, исключая при этом шум.

Вывод

Как показано, компоненты React очень тестируемы. Нет оправдания отказываться от написания хороших модульных тестов для ваших компонентов.

Приятно то, что JSX работает для вас в каждом отдельном тесте, а не против вас. С JSX вы можете передавать логические значения, обратные вызовы или все, что вам нужно. Имейте это в виду, когда вы решите самостоятельно протестировать компоненты React.

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

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