Краткое руководство по написанию сквозных тестов с использованием jest-puppeteer
В этой статье представлено краткое руководство по написанию эффективных сквозных тестов с использованием jest-puppeteer, с акцентом на процесс настройки, часто используемые API и практические сценарии тестирования на примере простой задачи-списка.
В рамках нашего стремления к обеспечению качества Logto и его непрерывному совершенствованию, мы используем jest-puppeteer для автоматизированного сквозного тестирования. Это позволяет нам быстро развивать Logto без прерываний.
В этой статье мы поделимся нашим опытом написания эффективных тестовых скриптов с использованием jest-puppeteer на примере простой задачи-списка. Наша цель — помочь вам быстро начать писать тестовый код jest-puppeteer для ваших собственных проектов.
Введение в сквозное тестирование с использованием jest-puppeteer
Сквозное тестирование — это способ убедиться, что ваше приложение правильно работает с точки зрения пользователя. Для достижения этого мы используем два основных инструмента: Jest и Puppeteer.
Jest — это популярный JavaScript-фреймворк для тестирования, который предлагает удобный API для написания тестов и утверждений. Он широко используется для юнит-тестирования и интеграционных тестов.
Puppeteer — это библиотека Node.js, разработанная командой Chrome, которая предоставляет высокоуровневый API для управления браузерами Chrome или Chromium без интерфейса. Это делает её идеальным выбором для автоматизации взаимодействия с браузером в сквозных тестах.
jest-puppeteer — это пресет для Jest, который позволяет проводить сквозное тестирование с использованием Puppeteer. Он предлагает простой API для запуска новых экземпляров браузеров и взаимодействия с веб-страницами через них.
Теперь, когда вы имеете базовое представление о необходимых инструментах, давайте приступим к настройке тестовой среды.
Настройка тестовой среды
Настройка тестовой среды для сквозного тестирования с использованием jest-puppeteer — это простой процесс, состоящий из трёх основных шагов:
- Установите зависимости.
В вашем проекте (или создайте новый проект) откройте терминал и выполните команду:
Затем перейдите в каталог node_modules/puppeteer
и установите Chromium (который нужен для работы Puppeteer):
- Настройте Jest.
Далее необходимо настроить Jest для бесшовной работы с Puppeteer.
Создайте файл конфигурации Jest (например, jest.config.js
) в корневом каталоге вашего проекта, если у вас его ещё нет. В этом файле укажите jest-puppeteer как преднастройку:
Вы можете настроить другие параметры Jest по мере необходимости в этом файле. Для получения дополнительной информации о настройке конфигурации Jest, пожалуйста, обратитесь к документации Jest.
- Напишите свои тесты.
Создайте тестовые файлы в вашем проекте, которые обычно называются с расширением .test.js
. Jest автоматически обнаружит и выполнит эти тестовые файлы.
Вот пример из документации Jest:
Затем выполните команду для запуска теста:
Выполнив эти три шага, вы получите хорошо настроенную тестовую среду для проведения сквозных тестов с использованием jest-puppeteer.
Однако учтите, что это лишь базовый пример. Для получения более подробной информации о настройке среды, пожалуйста, обратитесь к соответствующей документации:
Часто используемые API
В следующих шагах мы будем полагаться на API, предоставленные Jest, Puppeteer и jest-puppeteer, чтобы помочь нам в наших тестах.
Jest предоставляет API, в основном предназначенные для организации тестов и проверки ожидаемых результатов. Вы можете изучить конкретные детали в документации.
API Puppeteer в основном разработаны для взаимодействия с браузерами, и их поддержка тестирования может быть не такой очевидной. Вы можете обратиться к документации Puppeteer, чтобы понять функции, которые она предоставляет. В наших последующих примерах тестов мы рассмотрим некоторые типичные случаи использования.
Поскольку API Puppeteer изначально не были разработаны для тестирования, использование их для написания тестов может оказаться затруднительным. Чтобы упростить процесс написания тестов на Puppeteer, jest-puppeteer включает встроенную библиотеку под названием expect-puppeteer
. Она предлагает набор лаконичных и удобных API. Подробности о библиотеке приведены в её документации, которая предоставляет различные полезные функции, как показано в примерах ниже:
В следующих примерах мы объединим API, предоставленные этими библиотеками, чтобы выполнить наши тестовые сценарии.
Теперь пришло время начать учиться писать тестовый код на примере нашего простого приложения-задачи.
Простое приложение-задача
Предположим, у нас есть простое приложение-задача, работающее по адресу http://localhost:3000
, основная HTML-кодировка этого приложения выглядит следующим образом:
При проведении сквозного тестирования мы стремимся охватить следующие сценарии:
- При доступе к URL приложению, приложение и его данные должны загружаться корректно.
- Кнопка проверки задачи должна быть кликабельной.
- Клик по внешней ссылке в заметках задачи должен открыть ссылку в новой вкладке.
- Можно добавить задачу через форму.
Позже мы напишем тестовый код для проверки этих сценариев.
Учитывайте, что приложение и его данные загружены
Мы считаем, что приложение успешно загружено, когда выполнены следующие условия:
- После доступа к URL приложения на странице должен существовать элемент с идентификатором "app".
- Данные внутри приложения должны быть корректно отображены.
Итак, мы написали следующий тестовый код:
В коде:
page.goto
эквивалентен вводу "http://localhost:3000" в браузере, позволяя навигацию на любой URL.page.waitForSelector
используется для ожидания соответствия конкретного CSS-селектора с определённым элементом, с 30-секундным временем ожидания по умолчанию.expect(page).toMatchElement
используется из библиотекиexpect-puppeteer
, и аналогичноpage.waitForSelector
, но также поддерживает проверку текста внутри элемента. По умолчанию время ожидания дляtoMatchElement
составляет 500 мс.
Может показаться, что всё идеально, но при запуске этого теста и развертывании его в среде CI он иногда не проходит после нескольких попыток. Сообщение об ошибке указывает:
Основываясь на информации об ошибке и на том, что тест чаще всего проходит, можно сделать вывод, что данные, запрашиваемые приложением, могут не всегда возвращаться в течение первых 500 мс после загрузки приложения. Следовательно, мы хотим дождаться загрузки данных перед выполнением утверждения.
Обычно есть два общих подхода к достижению этой цели:
- Увеличьте время ожидания утверждения.
Из сообщения об ошибке видно, что время ожидания по умолчанию для toMatchElement
составляет 500 мс. Мы можем увеличить время ожидания, добавив параметр timeout
в функцию следующим образом:
Этот подход может сделать тесты менее часто провальными в некоторой степени, но он не решает проблему полностью, поскольку мы не можем точно сказать, сколько времени потребуется для загрузки данных.
Поэтому мы применяем этот подход только тогда, когда уверены во времени ожидания, например, в сценариях типа "всплывающее окошко появляется после наведения на элемент более чем на 2 секунды.".
- Дождитесь завершения сетевого запроса перед выполнением утверждения.
Это правильный подход. Хотя мы не знаем, сколько времени займёт запрос данных, ожидание завершения сетевого запроса всегда является надёжным выбором. В этот момент мы можем использовать page.waitForNavigation({ waitUntil: 'networkidle0' })
, чтобы дождаться завершения сетевых запросов:
Таким образом, прежде чем мы выполним утверждение для приложения и загруженных данных, мы можем убедиться, что наши сетевые запросы уже завершены. Это гарантирует, что тест постоянно даёт правильные результаты.
Ожидайте нажатия на конкретную кнопку
Далее мы тестируем функциональность нажатия на кнопку проверки внутри задачи.
В примерном приложении мы заметили, что все задачи имеют одинаковую структуру. Их кнопки проверки имеют CSS-селектор li[class$=item] > button
, а текст на кнопке всегда "Проверить". Это означает, что мы не можем напрямую указать, на какую именно кнопку задачи следует нажать. Поэтому нам нужен новый подход.
Предположим, мы хотим нажать на кнопку проверки задачи "Прочитать документацию по началу работы с Logto". Мы можем разбить это на два шага:
- Сначала получите ссылку на эту конкретную задачу.
- Затем нажмите на кнопку проверки внутри этой задачи.
Как показано в коде, мы используем функцию toMatchElement
, чтобы найти задачу с itemName
, установленным в "Прочитать документацию по началу работы с Logto". Затем мы используем ссылку readDocItem
, чтобы нажать кнопку с текстом "Проверить", находящуюся под ней.
Важно отметить, что при получении readDocItem
CSS-селектор li[class$=item]:has(div[class$=itemName])
используется для совпадения с корневым элементом li
задачи, а не с элементом itemName
внутри него, так как мы намереваемся нажать кнопку под тегом li
позже.
Использование expect(readDocItem).toClick
аналогично toMatchElement
. В примерном коде мы передаём { text: 'Проверить'}
, чтобы дополнительно совпадать с textContent
кнопки. Однако вы можете выбрать, нужно ли проверять текст кнопки в зависимости от ваших нужд.
Ожидайте, что внешняя ссылка будет открыта на новой вкладке
Далее мы хотим протестировать возможность открытия внешней ссылки на новой вкладке при нажатии на неё в заметках задачи.
В примерном приложе нии мы обнаружили, что задача "Прочитать документацию по началу работы с Logto" содержит внешнюю ссылку на документацию Logto в своих заметках. Вот наш тестовый код:
В коде мы используем toClick
, чтобы перейти по ссылке в itemNotes
.
Затем мы используем browser.waitForTarget
, чтобы поймать новую вкладку с URL "https://docs.logto.io/docs/tutorials/get-started".
После получения вкладки мы используем target.page()
, чтобы получить экземпляр страницы документации Logto и дополнительно проверить, загружена ли страница.
Также не забудьте закрыть вновь открытую страницу после завершения нашего теста.
Ожидайте создать задачу через форму
Теперь мы хотим протестировать функциональность добавления задачи.
Нужно заполнить поле имени задачи и заметок задачи в форме, нажать кнопку "Добавить" и проверить, отображается ли добавленный контент в списке:
Обратите внимание, что мы используем функцию expect(page).toFill(inputSelector, content)
для заполнения содержимого формы. Эта функция заменяет всё содержимое в поле ввода на content
.
Если вы хотите добавить несколько символов в поле ввода без замены существующего содержимого, вы можете использовать page.type(selector, content)
для непосредственного добавления желаемого содержимого в поле ввода.
Однако, когда у нас есть множество полей для заполнения формы, многократное использование toFill
неудобно. В этом случае мы можем использовать следующий подход для заполнения всего содержимого за один вызов:
Предоставленный ключ объекта — это атрибут name
элемента ввода.
Резюме
Мы рассмотрели простой пример для знакомства с типичными требованиями к тестированию и соответствующими методиками написания кода при использовании jest-puppeteer для сквозного тестирования. Мы надеемся, что это поможет вам быстро освоить основы написания тестового кода с использованием jest-puppeteer.