Как создавать надежные программы React с помощью TDD и библиотеки тестирования React

kak sozdavat nadezhnye programmy react s pomoshhyu tdd i biblioteki?v=1656623540

Одна вещь, с которой я боролся, когда начал изучать React, — это тестировать свои веб-приложения полезным и интуитивно понятным способом. Я использовал Enzyme из Jest для неглубокой визуализации компонента всякий раз, когда хотел его проверить.

Конечно, я совершенно злоупотреблял функцией тестирования снимков.

Ну, по крайней мере, я написал тест, да?

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

Недавно я посетил семинар через workshop.me с Кентом С. Доддсом, где он научил нас писать лучшие интеграционные тесты для приложений React.

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

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

Начинаем

Начнем с бега create-react-app и установление зависимостей Я предполагаю, что если вы читаете статью о тестировании программ, вы, вероятно, уже знакомы с установкой и запуском JavaScript. Я буду использовать yarn а не npm здесь.

create-react-app comment-feed
cd comment-feed
yarn

В этом случае мы можем удалить все файлы в файле src каталог кроме index.js. Затем прямо внутри src папку, создайте новую папку под названием components и еще одна папка призвана containers.

Для тестирования утилит я собираюсь создать это приложение с помощью библиотеки тестирования React Kent. Это легкая тестовая утилита, поощряющая разработчика тестировать свою программу так же, как она будет использоваться.

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

Мы больше не хотим проверять детали внедрения. Мы хотим воспроизвести компонент и увидеть, происходят ли правильные вещи, когда мы щелкаем или изменяем что-то в интерфейсе. Это! Больше никаких прямых проверок реквизитов или имен сословий или классов.

Установим их и приступим к работе.

yarn add react-testing-library

Давайте сделаем этот первый компонент в стиле TDD. Запустите свой тестовый бегун.

yarn test --watch

Внутри containers папке, мы собираемся добавить файл под названием CommentFeed.js. Рядом с ним добавьте файл под названием CommentFeed.test.js. Для первого теста давайте проверим, могут ли пользователи создавать комментарии. Слишком рано? Ладно, поскольку у нас еще нет кода, мы начнем с меньшего теста. Давайте проверим, можем ли мы воспроизвести канал.

Некоторые примечания к библиотеке тестирования реагирования

Сначала обратим внимание на функцию визуализации. Это похоже на способ react-dom отображает компонент в DOM, но возвращает объект, который мы можем деструктурировать, чтобы получить аккуратные помощники для тестирования. В этом случае получаем queryByTextкоторый, учитывая некоторый текст, который мы ожидаем увидеть в DOM, вернет этот HTML элемент.

Документы библиотеки React Testing Library имеют иерархию, которая поможет вам решить, какой метод запроса или использовать. Как правило, порядок выглядит так:

  • getByLabelText (входные данные формы)
  • getByPlaceholderText (только если ваш введенный текст не имеет метки – менее доступен!)
  • getByText (кнопки и заголовки)
  • getByAltText (изображение)
  • getByTestId (используйте это для таких вещей, как динамический текст или другие удивительные элементы, которые вы хотите проверить)

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

Если ни одно из них не дает вам именно то, что вы ищете, то render метод также возвращает элемент DOM, сопоставленный на container собственность, поэтому вы можете использовать его как container.querySelector(‘body #root’).

Первый имплементационный кодекс

Теперь реализация будет смотреться достаточно просто. Нам просто нужно убедиться, что канал комментариев есть в компоненте.

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

Этот следующий тест подтвердит, что мы можем давать комментарии. Но у нас даже нет комментариев, поэтому давайте также добавим этот компонент. Хотя после теста.

Я также намерен создать объект props для хранения данных, которые мы можем повторно использовать в этих тестах.

В этом случае я проверяю, равно ли количество комментариев равно количеству элементов, переданных в CommentFeed. Это тривиально, но провал теста позволяет нам создать файл Comment.js.

Это зелёный свет в нашей тестовой программе, чтобы мы могли продолжать без опасений. Поздравляю всех TDD, спасителя нашего рода. Конечно, это работает, когда мы придаем ему пустой массив. Но что если мы дадим ему какие-то реальные объекты?

Мы должны обновить нашу реализацию, чтобы фактически отражать вещи. Теперь достаточно просто, чтобы знать, куда мы идем, не правда ли?

О, посмотрите, наш тест снова проходит. Вот хороший снимок его красоты.

1*vGkFKnUkA9ms5PbaOWoQ_A

Обратите внимание, как я ни разу не сказал, что мы должны запустить нашу программу yarn start? Мы будем так оставаться некоторое время. Дело в том, что вы должны почувствовать код своим разумом.

Стиль – это лишь то, что снаружи, важно то, что внутри.

Если вы хотите запустить приложение, обновите index.js до следующего:

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

Давайте начнем с описания того, что я хочу от этой формы. Это должно:

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

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

Обратите внимание на то, как развивается наш тестовый пакет? Мы перешли от жесткой кодировки реквизитов в их собственных тестовых случаях к созданию для них фабрики.

Упорядочивать, действовать, утверждать

Этот интеграционный тест можно разбить на три части: упорядочить, действовать и подтвердить.

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

Существуют некоторые предположения относительно кода, например, имена наших меток или тот факт, что мы будем иметь createComment опора.

Находя входящие данные, мы хотим попытаться найти их по меткам. Это предпочитает доступность, когда мы создаем наши программы. Самый простой способ получить форму с помощью container.querySelector.

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

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

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

Это пугает? Не надо бояться, дитя мое, иди по этому пути. Начните с добавления формы в функцию визуализации.

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

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

Вспомните, когда мы реорганизовали наш набор тестов, создав файл createProps завод? Вот так. Мы можем также реорганизовать тесты.

Теперь добавим handleChange и handleSubmit методы класса Они увольняются, когда мы изменяем введенные данные или отправляем нашу форму. Я тоже инициализирую наше государство.

И это сделало это. Наши тесты проходят, и у нас есть что-то похожее на настоящую программу. Как выглядит покрытие?

1*Q4coAIT2yaP120pDWGxoAQ

Неплохо. Если мы проигнорируем все настройки, содержащиеся в index.js, мы получим полностью охваченную веб-приложение относительно выполняемых строк.

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

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

Пользователь, прошедший «аутентификацию», получит их auth имущество, передаваемое через заявку. Любые действия, имеющие отношение к проверке подлинности, будут отмечены.

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

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

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

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

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

Давайте «лайкнем» комментарий. Добавьте следующий тестовый пример, затем обновите фабрику реквизитов likeComment.

А теперь, что касается реализации, мы начнем с обновления компонента «Комментарий», чтобы он имел кнопку «нравится», а также data-testid атрибут, чтобы мы могли найти его.

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

Теперь мы просто добавляем этот метод класса к нашему CommentFeed:

Вы можете удивиться, почему мы просто не передаем likeComment поддерживайте непосредственно компонент Comment. Почему мы делаем его свойством класса?

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

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

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

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

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

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

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

Ну я немного обманул: где мы проходили author к onLike функцию раньше, я изменил на currentUserчто является auth prop передается компоненту Comment.

В конце концов не было бы смысла, чтобы автор комментария появлялся, когда кому-то нравится его комментарий.

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

Но здесь нет невежества, только тесты и следующий код. Обязательно обновите CommentFeed, чтобы он предусматривал передачу auth собственность. Для onClick обработчиков мы можем пропустить передачу auth свойства, поскольку мы можем получить это из auth имущество у родителей handleLike и handleDislike методы

Подведению

Надеемся, ваш тестовый набор выглядит как незажженная елка.

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

Например, скажите, что вы действительно хотите реализовать handleLike и handleDislike в одном методе класса, но сейчас у вас другие приоритеты. Вы можете сделать это, задокументировав в тестовом примере, например:

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

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

Надеюсь, это вас на время успокоит.

Хотите получить дополнительные сообщения или остроумные замечания? Если вам понравилась эта статья, дайте мне несколько хлопаний и следите за мной на Medium, Github и Twitter!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *