Почему page.goto() замедляет ваши тесты Playwright
Когда вы тратите время и усилия на создание хорошо работающего комплексного набора тестов или внедряете синтетический мониторинг Playwright с помощью Checkly, вам следует сосредоточиться на двух вещах.
Ваши тесты должны быть стабильными, потому что нет ничего хуже, чем ненадежный набор тестов. Но также ваши тесты должны быть быстрыми, потому что никто не хочет часами ждать, чтобы получить зеленый свет, когда вы находитесь в напряжении, чтобы развернуть это важное производственное исправление.
Что, если я скажу вам, что вы замедляете свои тесты с помощью самого простого действия Playwright — page.goto()
?
Посмотрите это видео или читайте дальше, чтобы узнать больше. Оба включают примеры и способы ускорить ваши тесты по драматургу.
Готовый? Начнём!
Начало каждого сценария драматурга — page.goto()
.
Вот довольно простой сценарий Playwright.
import { expect, test } from "@playwright/test";
test("Login works", async ({ page }) => {
await page.goto("/");
const loginLink = page.getByRole("link", { name: "Login" });
await loginLink.click();
const loginHeader = page.getByRole("heading", { name: "Login" });
await expect(loginHeader).toBeVisible();
// More things to check after the login link was clicked
// ...
});
Он переходит к корню базового URL-адреса, определенного в файле playwright.config
. Он находит ссылку, щелкает по ней, а затем проверяет, был ли соответствующим образом обновлен пользовательский интерфейс сайта (я скрыл большинство действий и утверждений, поскольку они не имеют значения для этого поста).
Все это прекрасно работает, и я уверен, что вы уже писали подобный код Playwright. Если вы проверите свои тесты и их продолжительность, вы, возможно, обнаружите, что время выполнения page.goto()
может сильно различаться.
Пример:
В приведенной выше трассировке Playwright вызов page.goto()
занимает десять секунд! Удивительно, но полоса фильма в верхней части средства просмотра трассировки показывает, что сайт-пример был виден почти все время ожидания.
И это единственный тест для сверхскоростного местного сайта электронной коммерции. Тестирование всех основных функций магазина разумного размера может легко включать сотню тестов, а затем такая небольшая задержка приводит к более чем 15-минутному времени ожидания в вашем конвейере CI/CD
!
(Конечно, вы можете сократить это время ожидания, распараллелив тесты, но аргумент о ненужном ожидании остается в силе.)
Чего мы ждем?
page.goto()
и событие load
Если вы проверите документацию Playwright, вы обнаружите, что по умолчанию page.goto()
ожидает события загрузки.
await page.goto("/");
// is the same as
await page.goto("/", {waitUntil: "load"});
Что такое событие load
? MDN дает нам ответ.
Событие загрузки вызывается при загрузке всей страницы, включая все зависимые ресурсы, такие как таблицы стилей, сценарии, iframe
и изображения.
Давайте обдумаем это. Всякий раз, когда вы вызываете page.goto()
, вы ожидаете загрузки всех таблиц стилей, скриптов, iframe
и изображений. Короче говоря, вы ждете, пока все (!) загрузится, прежде чем запускать тест.
При проверке вкладки сети в моем примере файла трассировки я обнаружил изображение SVG, задерживающее все тестовые действия. Почему этот файл такой медленный? Я не знаю, но всегда есть вероятность того, что ресурсы загружаются дольше.
На данный момент это всего лишь небольшое демонстрационное приложение. Представьте себе полноценный интернет-магазин; там будут сотни изображений, не говоря уже о всех скриптах, пришедших «откуда-то». Для такого сайта конфигурация page.goto()
по умолчанию будет ждать, пока все ресурсы будут пропущены через сеть, прежде чем нажать первую кнопку. Это лучший способ?
Ваши пользователи не ждут ваших запросов — почему это должны делать ваши тесты?
Давайте сделаем шаг назад на мгновение…
Почему вы постоянно тестируете и отслеживаете свои сайты с помощью реальных браузеров? Конечно, вы хотите убедиться, что ваш сайт и инфраструктура работают правильно. Но вы также можете сделать это, имитируя реальное поведение пользователя. В этом весь смысл сквозного тестирования, верно?
Ваши пользователи ждут, пока маленький счетчик в строке URL-адреса исчезнет, прежде чем они нажмут на что-нибудь? Я сомневаюсь в этом. Я почти уверен, что средний посетитель не будет смотреть на состояние загрузки вашего сайта, прежде чем нажать кнопку «Добавить в корзину». Люди нажимают на ваш сайт и взаимодействуют с ним всякий раз, когда что-то видно. Вот почему лучший подход к написанию сценариев Playwright — придерживаться естественного человеческого поведения.
Как же тогда проводить тесты?
Поведение ожидания page.goto()
можно настроить с помощью свойства конфигурации waitUntil
. И вы можете определить четыре параметра waitUntil
.
// Wait until the HTML starts loading.
await page.goto("/", {waitUntil: "commit"});
// Wait until the HTML is parsed
// and deferred scripts (`<script deferred>` and `<script type="module">`) are loaded.
await page.goto("/", {waitUntil: "domcontentloaded"});
// Wait until all initially included resources are loaded.
await page.goto("/", {waitUntil: "load"});
// Wait until every resource is loaded and the network is silent for 500ms.
await page.goto("/", {waitUntil: "networkidle"});
Когда вы смотрите на эти параметры waitUntil
, все, кроме фиксации, в значительной степени зависят от сети и встроенных ресурсов. Это означает, что все параметры, кроме деталей реализации проверки фиксации, потенциально замедляют ваши тесты, когда один запрос застревает на сетевом уровне.
Как эти варианты сравниваются по скорости? Вот результаты моего примера тестового примера.
Вариант | Bремя выполнения page.goto |
commit | 62ms |
domcontentloaded | 159ms |
load | 10.1s |
networkidle | 12.6s |
Сравнение скорости waitUntil
Конечно, я извлек эти цифры из местного теста драматурга, проведенного на местном сайте. Но абсолюты не имеют значения. Важно взглянуть на различия между опциями waitUntil
.
Неудивительно, что опция ожидания фиксации является самой быстрой конфигурацией goto()
, поскольку она не ожидает ничего, связанного с ресурсами, кроме первых байтов исходного HTML. networkIdle
на сегодняшний день является самым медленным, поскольку он ожидает загрузки каждого ресурса, а затем добавляет 500 мс времени простоя сети.
Но вот что забавно: все эти тесты прошли успешно независимо от опции waitUntil. Общая продолжительность теста для этого небольшого тестового примера варьировалась примерно от 10 до 25 секунд, но все тесты были зелеными.
Что происходит и как это работает?
Написание быстрых тестов, не зависящих от сети
По моему опыту, есть только две причины ненадежных или медленных тестов: либо ваше приложение не поддается тестированию, либо вы не следуете лучшим практикам Playwright.
Если сайт, который вы хотите протестировать, нестабилен, вам не удастся создать стабильный и быстрый набор тестов. Вы не можете написать стабильные тесты для нестабильного приложения — конец истории. Точно так же, если у вашего сайта плохой UX и плохие шаблоны гидратации, ваши тесты будут полны обходных путей, позволяющих добиться успеха на счастливом пути. В идеале вы бы исправили эти проблемы с приложениями, но я знаю, что это не всегда возможно.
Но если вы, с другой стороны, не ставите приоритет пользователя в своих сценариях драматурга, вы также будете преступником. Лучший способ ускорить процесс и бороться с ним — полагаться на автоматическое ожидание и утверждения, ориентированные на веб-сначала, а остальное пусть сделает Playwright.
Пример сценария в начале статьи включает «простую» инструкцию click()
. И этот метод click()
— ваш лучший друг, потому что он выполняет магию, то есть проверку работоспособности.
// this click() will wait for the login to be
// - visible
// - stable, as in not animating or completed animation
// - able to receives events as in not obscured by other elements
// - enabled
await loginLink.click();
Всякий раз, когда вы вызываете click()
, fill()
или другие действия, Playwright ждет, пока этот элемент будет готов для пользователя. На этом этапе элемент визуализируется, становится видимым, стабильным и включенным.
Если ваше приложение теперь обеспечивает хороший UX и отображает готовые к использованию элементы, Playwright будет взаимодействовать с ними как можно быстрее. Нет необходимости ждать каких-либо сетевых запросов.
Аналогично, если вы полагаетесь на утверждения, ориентированные на веб-сначала, Playwright будет ждать, пока пользовательский интерфейс не достигнет желаемого состояния. Также нет необходимости ждать вызовов HTML или API для определенного состояния пользовательского интерфейса.
// wait until this element is visible
await expect(loginHeader).toBeVisible();
И если вы полагаетесь на эти два основных принципа драматурга, вы можете забыть о сетевом уровне и протестировать то, что важно — действия пользовательского интерфейса и результирующее состояние пользовательского интерфейса. Едва ли нужно ждать сетевых событий и запросов.
import { expect, test } from "@playwright/test";
test("Login works", async ({ page }) => {
// don’t wait for all the resources to be loaded
await page.goto("/", {waitUntil: "commit"});
// let Playwright wait for this link to be visible
const loginLink = page.getByRole("link", { name: "Login" });
await loginLink.click();
const loginHeader = page.getByRole("heading", { name: "Login" });
await expect(loginHeader).toBeVisible();
// More things to check after the login link was clicked
// ...
});
Глядя на скорректированный скрипт, мы ждем не сетевых событий, а состояния пользовательского интерфейса. Драматург подождет, пока не появится ссылка «Войти», и нажмет на нее как можно быстрее. И с помощью этой незначительной настройки мы ускорили наши тесты на 10 секунд, сохраняя при этом основные функции входа в систему. Беспроигрышный вариант!
Но... Всегда есть но...
Тем не менее, существуют сценарии, когда вы хотите следить за сетью.
Когда вы внедряете сквозное тестирование, очень часто тестируют предварительные развертывания, чтобы избежать регресса основных функций. В идеале ваши среды предварительного просмотра и промежуточные среды являются производственными копиями. К сожалению, это случается редко. Промежуточный этап часто развертывается в другой инфраструктуре, а внешний интерфейс часто загружает разные ресурсы для отслеживания, взаимодействия с пользователями и мониторинга.
И эти различия могут быть приемлемыми, потому что вы хотите только протестировать новую функцию и избежать регрессий. Но когда вы сделаете все возможное и примените Playwright для мониторинга своих сайтов, наблюдение за всеми загруженными ресурсами поможет вам избежать производственных проблем. Я не раз видел, как сторонний скрипт останавливал производство.
Например, мы в Checkly отслеживаем checklyhq.com
с помощью Playwright и, как и любой маркетинговый сайт, включаем туда Intercom и аналитику. Однажды все наши синтетические проверки драматурга провалились, потому что Интерком не смог загрузиться в разумные сроки. page.goto()
заняло больше минуты из-за тайм-аута фрагмента JavaScript.
Checklyhq.com
сломался? Нет, все по-прежнему функционировало. Было ли приятно узнать, что у Intercom возникли проблемы? Держу пари!
Мониторинг сетевого уровня может дать вам ценную информацию об общем состоянии и производительности вашего сайта.
// fail when all resources aren’t loaded after 10 seconds
await page.goto("/", { waitUntil: "load", timeout: 10_000 });
Совет для профессионалов: тщательно агрегируйте основные веб-показатели в ваших сценариях драматурга.
Заключение
Должны ли вы теперь перевернуть каждое действие page.goto()
на фиксацию или загрузку domcontentloaded
, чтобы сэкономить время в CI/CD
? Ответ обычный: «Это зависит».
Если вы цените скорость выполнения тестов, запускаете много тестов и полагаетесь на тестирование с автоматическим ожиданием, ориентированное на пользователя, отсутствие ожидания загрузки всех ресурсов сэкономит вам минуты, если не часы, в CI/CD
. Попробуйте, Playwright очень хорошо умеет определять, когда и что нажать.
Но помните, что зеленый индикатор развертывания предварительного просмотра не дает вам должной защиты. Единственное, что имеет значение, — это хорошо работающая производственная среда. Чтобы гарантировать отсутствие производственных проблем, вы должны постоянно тестировать свой работающий продукт. Именно здесь на помощь приходит синтетический мониторинг.
А когда вы затем тестируете и отслеживаете свой сайт с помощью Playwright, наблюдение за медленными сетевыми зависимостями может быть очень полезным, поскольку вы первым узнаете, когда что-то не так с вашей инфраструктурой, загруженными сторонними ресурсами или вашим приложением. код. Это настоящая страховка, которая вам нужна, чтобы хорошо спать по ночам.