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

Если вы хотите пропустить все введение и объяснения, смело переходите к основанию или просто посетите репо. Однако, если вы хотите уйти с лучшим пониманием, наслаждайтесь чтением всей истории.

Итак, все уже подключили (посмотрите, что я там сделал) с помощью хуков React и других n ew функций React. Если у вас не было возможности поиграться с ними, вы можете начать с ознакомления с документацией. Как всегда, это действительно помогло.

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

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

Перейдем к Кодексу!

Прежде чем мы начнем, вам следует знать несколько вещей:

  • Мы будем использовать пакет react-hooks-testing-library. Чтобы использовать этот пакет, вам также необходимо установить react-test-renderer. Не забудьте добавить их как зависимости dev.
  • Как указано в файле readme, вы должны только делать это, если ваша ловушка не связана напрямую с компонентом или ее сложно протестируйте его посредством взаимодействия с пользовательским интерфейсом.
  • Если ваш хук включает в себя некоторые асинхронные операции, я рекомендую вам проверить этот фрагмент https://medium.com/@jantoine/another-take-on-testing-custom-react-hooks-4461458935d4, автор Antoine Jaussoin .

Мы будем использовать этот прекрасный прием useKeyPress для демонстрации. Хотя этот хук в значительной степени не требует пояснений, он возвращает объект с:

  • PressKeys: массив нажатых клавиш.
  • setPressedKey: функция для добавления клавиши к PressKeys.
  • removeDuplicateKeys: функция для удаления дубликатов в PressKeys.
  • clearPressedKeys: функция, очищающая нажатые клавиши.

У нас есть простой, глупо выглядящий компонент, позволяющий возиться с этим:

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

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

Одна из первых вещей, которые мы попытались сделать, - это вызвать ловушку прямо в нашем тесте, как это.

Можете ли вы понять, каков будет результат теста? Если вы похожи на большинство из нас, вы запустили его и столкнулись с такой ошибкой:

Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM)
 2. You might be breaking the Rules of Hooks
 3. You might have more than one copy of React in the same app
 See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.

Позор нам! Это довольно ясно 😢. Вы не можете использовать ловушку вне функции ловушки или компонента React. Итак, какие у нас есть варианты? Мы могли бы создать компонент-оболочку (у которого была бы еще одна загвоздка) или мы могли бы использовать пакет react-hooks-testing-library, чтобы облегчить себе жизнь.

Хорошо, что мы здесь делаем? По сути, мы импортируем renderHook из react-hooks-testing-library; функция-оболочка, которая поможет нам (кстати, прекрасно названа) «рендеринг» нашего хука.

После этого вместо прямого вызова useCustomHook мы передадим его в renderHook в качестве параметра. renderHook вернет объект, который мы превратим в result.

Наконец, у нас есть текущий объект внутри result, который содержит объект, возвращенный нашим хуком, поэтому мы можем легко утверждать значение PressKeys.

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

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

Warning: An update to TestHook inside a test was not wrapped in act(…).
 
 When testing, code that causes React state updates should be wrapped into act(…):
 
 act(() => {
 /* fire events that update state */
 });
 /* assert on the output */

Хорошо, достаточно честно. Чтобы обернуть наш вызов setPressedKey в этом act методе, нам просто нужно добавить его в импорт react-hooks-testing-library.

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

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

Наконец, вот ссылка на полный репозиторий, если вы хотите получить рабочий пример: https://github.com/falecci/plain-js-hooks-testing.

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

Если вы живете в Аргентине и хотите работать над действительно сложным продуктом, приходите и присоединяйтесь к нам в Morean. Мы работаем с React, Node и Python над бессерверной архитектурой на AWS, получаем массу удовольствия, не отставая от новейших технологических стеков и создавая отличный продукт.

Надеюсь, вам понравилась статья и вы нашли ее полезной. Как я сказал во вступлении, мы все еще идем своим путем с Hooks, поэтому, если у вас есть предложения, не стесняйтесь оставлять комментарии!

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