Тестирование в React: лучшие практики, советы и хитрости

Работать в среде пользовательского интерфейса уже не так просто, особенно в современную эпоху, когда преобладают сложные интерфейсные приложения. И это не всегда весело.

С «появлением» React многое изменилось, особенно в том, что касается представления пользовательского интерфейса с точки зрения компонентов. А как насчет тестирования?

Подождите, тестируете JavaScript? Кто так делает?

Что ж, это могло быть 10 лет назад, сейчас это совсем другая история. Как мы все знаем, тестирование таких сложных пользовательских интерфейсов и приложений очень важно. И остановитесь на таких оправданиях, как «у меня были сжатые сроки, и я не мог найти время для написания тестов». Написание тестов - это обычно первое, с чего вам следует начать, даже если вы не строго следуете TDD.

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

Тестовый бегун

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

Если вы не используете Jest, настоятельно рекомендуем перейти на него. Он действительно шагает вперед с точки зрения «проверки X восприимчивости» и обладает множеством потрясающих мощных функций.

Я также предполагаю, что вы используете Enzyme в качестве библиотеки тестов React. Думаю, это говорит само за себя.

Структурирование теста

На этом этапе вы готовы приступить к написанию тестов. Если вы занимаетесь TDD, вы все равно делаете это для начала.

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

Это хорошо, но мы можем добиться большего.

Например, основное описание «Core :: Buttons :: LinkButton» может быть полезно для «однозначной» идентификации этого конкретного теста в выходных данных средства выполнения тестов. К счастью, с Jest (и, вероятно, с другими участниками тестирования) в этом больше нет необходимости. Jest группирует все тесты по именам файлов. Если тест не прошел, вы получите «путь» к провалившемуся утверждению, а также много полезной информации. Это одна из тех вещей, которые мне больше всего нравятся в Jest, они действительно проделали потрясающую работу.

Как видите, наличие целевых тестовых утверждений действительно полезно. Это не только упрощает чтение теста, но и упрощает определение конкретного утверждения.

Поэтому не бойтесь вложения различных «описаний» и разделения ваших утверждений на несколько блоков «it».

Мы обнаружили, что полезно, когда каждый блок «это» содержит только одно утверждение. Это позволяет правильно задокументировать каждый тест, используя блок «it» вместо использования сообщения подтверждения.

Что мы также делаем, так это группируем основные «описывает» по какому-то контексту, который обычно выглядит примерно так:

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

Этот подход называется RDD (Readme Driven Development).

«описывает» предназначены для объяснения условий, тогда как «его» предназначены для объяснения ожидаемого результата.

Взгляните на этот пример:

Очень легко читать и понимать, что происходит. Это также позволяет иметь определенные настройки в каждом вложенном «описать» (например, визуализированные свойства компонентов, фиктивный импорт и т. Д.) С использованием «beforeEach».

Говоря о «beforeEach», он становится действительно полезным, когда вы тестируете различные состояния своего компонента. Посмотри на это:

Что нужно помнить, если вы используете Jest

  • предотвращение ошибок порядка выполнения - будьте осторожны, чтобы не смешивать код, расположенный рядом с «описывает» и «его» с хуками «beforeEach». . Поскольку хуки являются асинхронными, Jest сначала выполнит синхронный код вместо «beforeEach», как вы ожидали.
  • предотвращение зависимых тестов - убедитесь, что каждый блок «it» может выполняться изолированно. Последняя версия Jest также предоставляет простую команду в режиме «слежения» для запуска определенных тестов в рамках набора тестов.

Совет по увеличению продуктивности

Если вы используете iTerm, вы можете «cmd + click» на пути к файлу в трассировке стека, в случае сбоя теста, чтобы перейти к файлу на определенной строке.

Перейдите в «Настройки› Профили ›{profile}› Дополнительно ›Semantic History» и укажите (если вы используете Atom):

/usr/local/bin/atom \1:\2

Мелкая vs. маунт

У вас настроена структура тестирования. Теперь вы можете начать тестирование вашего компонента React.

Если вы только начинаете тестирование в React, вы, вероятно, столкнетесь с одной дилеммой: как визуализировать компонент для его тестирования? Enzyme предоставляет функции «поверхностного» и «крепления». Какой из них использовать?

На этот вопрос действительно легко ответить: используйте «мелкий»! В 99% случаев это то, что вы хотите. Должна быть чертовски веская причина для использования «mount». Вы даже можете увидеть это как запах кода.

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

Даже если вы хотите протестировать компонент в его смонтированном состоянии, вам не нужно использовать «mount»:

Данные испытаний

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

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

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

Издевательства и шпионы

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

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

Вы можете многое сделать со шпионами. С помощью Jest при вызове «jest.fn ()» вы можете передать реализацию этой функции, которая выполняет / возвращает все, что вам нужно.

В приведенном ниже примере функция «fetchData» обеспечивает обратный вызов, который вызывается после завершения выборки данных. Мы можем использовать «jest.fn ()» для имитации успешной выборки данных, выполняя обратный вызов вручную. Это позволяет нам тестировать любой код, запускаемый в результате успешной выборки данных (например, показывать уведомление, обновлять состояние, перенаправлять на другой маршрут и т. Д.).

Если вы не используете Jest, вам, вероятно, следует использовать sinon как вашу шпионскую / издевательскую библиотеку. Если вы действительно используете Jest, у вас есть все это из коробки. Кроме того, Jest предоставляет действительно мощную встроенную систему имитации модулей.

Тестирование снимков

А как насчет снэпшот тестирования? Разве мы не могли просто протестировать все визуализированные компоненты с помощью теста моментальных снимков? Верно, но это не всегда то, что вам нужно. Моментальное тестирование - это здорово, и мы должны его использовать, но делать это нужно осторожно и только тогда, когда это имеет смысл.

С большой властью приходит большая ответственность

В общем, я бы сказал, что мы должны явно указывать, какие элементы отображаются или какие свойства передаются этим элементам, даже если это немного многословно. В приведенном ниже примере мы хотим убедиться, что кнопка «Button» отключена, если достигнут определенный предел. Если мы явно проверим это, мы увидим, как выходной журнал помогает нам понять контекст («рендеринг› ‹Button› ›при достижении лимита› должен отключить ‹Button› »).

Если бы мы тестировали весь компонент только с помощью теста моментального снимка, он все равно не прошел бы (при тех же обстоятельствах), но вы бы потеряли контекст, связанный с неудачным тестом («рендеринг› выводит правильное дерево ”). Вы больше не знаете, почему нужно отключить «Button».

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

При использовании только тестирования моментальных снимков вы…

  • потеряете способность выполнять TDD - ну, у вас не может быть моментального снимка того, чего еще не существует
  • потерять способность выполнять RDD - тестовый файл больше не описывает, что важно отрисовать в компоненте
  • не знаете, каким «должен быть» фактический результат, вы знаете только то, что «было» раньше.

Расширенные тестовые случаи

Реагировать на элементы как на опоры

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

Так как бы вы протестировали этот реквизит? В приведенном выше примере компонент отображает «Table» или «Button», но из-за неглубокой визуализации все остальное не будет отображаться.

Вот как вы можете решить эту проблему - просто требуется немного больше настройки.

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

Компоненты FaC

Вы, наверное, уже слышали об этом: Функции как дочерние компоненты. Если нет, есть много статей, объясняющих, что это / делает. Или вы видели это раньше, но не знали, что это так называется.

Возьмем простой пример:

Компонент «Collapsible» в основном управляет состоянием переключения и возвращает логический флаг «isOpen», чтобы указать текущее состояние, а также « toggle ”для обновления состояния.

Представьте, что вы хотите протестировать компонент «CollapsiblePanel», который использует компонент «Collapsible». Поскольку вы тестируете компонент «CollapsiblePanel» с неглубокой визуализацией, дочерняя функция «Collapsible» не будет выполняться. Как тогда это проверить?

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

Теперь все, что вам нужно сделать, это напрямую вызвать функцию «renderPanel» и протестировать возвращаемое значение как обычный компонент React.

Заключение

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

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