Потребность в быстрых и отзывчивых приложениях как никогда высока в связи с переходом от настольных компьютеров к мобильным. При этом сложность и размер веб-приложений постоянно растут. Понятно, почему тема производительности веб-страниц сегодня актуальна
Эта статья дает базовое представление о тестировании производительности веб-сайтов в Playwright.
Содержание:
Скорость важна
Время, необходимое для продуктивной работы с онлайн-сервисом, влияет на восприятие пользователя. Полезные функции, великолепный дизайн и другие характеристики становятся неважными, если сервис работает медленно — и пользователи уходят.
Вы можете создать лучшее в мире приложение, но помните: у каждого пользователя есть ограниченное количество времени, которое он может потратить на решение своих проблем с помощью вашего сервиса. Превысив это количество, вы рискуете потерять пользователей, уступив другому, более производительному сервису. Особенно важна производительность для новых пользователей, которые еще не успели убедиться в качестве вашего сервиса.
БЕСПЛАТНО СКАЧАТЬ КНИГИ в телеграм канале Библиотека тестировщика
Конкурентное преимущество
У этого есть и светлая сторона: если низкая производительность может потопить онлайн-платформу, то высокая производительность вполне может помочь ей подняться в топ. Скорость и отзывчивость могут стать отличительной чертой сервиса, побуждая пользователей предпочесть его конкурентам. Поэтому инвестиции в эту область почти всегда окупаются. Некоторые примеры из практики известных компаний:
- Pinterest сократил время ожидания пользователей и увеличил трафик и конверсию.
- Компания Zalando применила небольшие меры по улучшению времени загрузки и обнаружила прямую корреляцию с увеличением дохода за сессию.
- BBC обнаружила, что каждая лишняя секунда загрузки страницы приводила к тому, что 10 % пользователей покидали ее.
Измерение производительности
Учитывая важность производительности, неслучайно браузеры предоставляют множество метрик производительности. Знание этих метрик с учетом времени обеспечит вам обратную связь, необходимую для поддержания производительности для ваших пользователей.
Для получения наиболее полной информации можно комбинировать несколько подходов:
- Мониторинг реальных пользователей позволяет понять, какую производительность испытывают конечные пользователи вашего сервиса.
- Синтетический мониторинг для проактивного сбора информации о производительности сервиса и обнаружения проблем до того, как с ними столкнутся пользователи.
- Собственно Тестирование производительности, чтобы избежать деградации производительности, на проде в первую очередь.
- Регулярные аудиты для получения оценки эффективности и предложений по ее улучшению, например, с помощью таких инструментов, как Google Lighthouse.
Метрики производительности Google’s Web Vitals
Поскольку Google стремится обеспечить более быстрый Интернет, метрики Web Vitals всегда должны быть в поле вашего зрения. Такие показатели, как Время до первого байта (TTFB), Общее время блокировки (TBT) или Первый значимый рисунок в контенте (FCP), являются хорошими индикаторами пользовательского опыта и заслуживают внимания.
Google рекомендует обратить внимание на три наиболее важных показателя: Скорость отрисовки самого большого элемента контента (LCP), Задержка до первого пользовательского ввода (FID) и Общий сдвиг разметки (CLS). Эти три показателя считаются основными метриками веб-страницы и дают хорошее представление о скорости ее загрузки, интерактивности и визуальной стабильности.
Но не все метрики Google Web Vitals подходят для синтетического мониторинга и тестирования производительности. Задержка до первого пользовательского ввода (FID) зависит от взаимодействия с пользователем, и ее лучше всего измерять с помощью мониторинга реальных пользователей. Используйте Общее время блокировки (TBT) в качестве метрики интерактивности при внутреннем тестировании в компании.
Оценка производительности веб-сайтов с помощью headless-инструментов
Как бы мы ни стремились создавать быстрые приложения, мы должны выполнять задачи по мониторингу и тестированию производительности, чтобы обеспечить непрерывную обратную связь и быстрое вмешательство в случае ухудшения метрик. Playwright предоставляет отличный набор инструментов для синтетического мониторинга и тестирования производительности:
- Доступ к API-интерфейсам Web Performance.
- При тестировании на Chromium — доступ к протоколу Chrome DevTools для проверки трафика, эмуляции сети и пр.
- Совместимость с библиотеками производительности из экосистемы Node.js.
API-интерфейсы Web Performance
Современные браузеры поддерживают множество API-интерфейсов Web Performance и Web Vitals.
API навигации и синхронизации ресурсов
API производительности Navigation Timing и Resource Timing являются спецификациями W3C. Документация MDN определяет область применения обоих:
Navigation timings — это метрики, измеряющие события навигации по странице в браузере. Resource timings — это подробные измерения сетевого времени загрузки ресурсов приложения. Оба предоставляют одинаковые свойства, доступные только для чтения, но Navigation timings измеряет тайминги главного документа, в то время как Resource timings измеряет время для всех ресурсов, запрашиваемых и загружаемых этим главным документом.
API Navigation Timing позволяет выводить метки ключевых событий на временнОй шкале загрузки страницы. Запись Navigation Timing включает такие метрики, как время отклика навигации, используемый протокол и время загрузки документа.
const { chromium } = require('playwright')
;(async () => {
const browser = await chromium.launch()
const page = await browser.newPage()
await page.goto('https://danube-web.shop/')
const navigationTimingJson = await page.evaluate(() =>
JSON.stringify(performance.getEntriesByType('navigation'))
)
const navigationTiming = JSON.parse(navigationTimingJson)
console.log(navigationTiming)
await browser.close()
})()
Консольный вывод
[{
name: 'https://danube-web.shop/',
entryType: 'navigation',
startTime: 0,
duration: 1243.7999999998137,
initiatorType: 'navigation',
nextHopProtocol: 'http/1.1',
workerStart: 0,
redirectStart: 0,
redirectEnd: 0,
fetchStart: 0.10000000009313226,
domainLookupStart: 1.2000000001862645,
domainLookupEnd: 11.100000000093132,
connectStart: 11.100000000093132,
connectEnd: 336.8000000002794,
secureConnectionStart: 102.89999999990687,
requestStart: 336.89999999990687,
responseStart: 432.39999999990687,
responseEnd: 433.70000000018626,
transferSize: 971,
encodedBodySize: 671,
decodedBodySize: 671,
serverTiming: [],
workerTiming: [],
unloadEventStart: 0,
unloadEventEnd: 0,
domInteractive: 1128.8999999999069,
domContentLoadedEventStart: 1128.8999999999069,
domContentLoadedEventEnd: 1130.8999999999069,
domComplete: 1235.3999999999069,
loadEventStart: 1235.3999999999069,
loadEventEnd: 1235.3999999999069,
type: 'navigate',
redirectCount: 0
}]
API Resource Timing позволяет рассмотреть внимательнее отдельные ресурсы и получить точную информацию о том, как быстро они загружаются. Например, логотип сайта:
const { chromium } = require('playwright')
;(async () => {
const browser = await chromium.launch()
const page = await browser.newPage()
await page.goto('https://danube-web.shop/')
const resourceTimingJson = await page.evaluate(() =>
JSON.stringify(window.performance.getEntriesByType('resource'))
)
const resourceTiming = JSON.parse(resourceTimingJson)
const logoResourceTiming = resourceTiming.find((element) =>
element.name.includes('.svg')
)
console.log(logoResourceTiming)
await browser.close()
})()
Вывод в консоли:
{
name: 'https://danube-web.shop/static/logo-horizontal.svg',
entryType: 'resource',
startTime: 1149.1000000000931,
duration: 96.89999999990687,
initiatorType: 'img',
nextHopProtocol: 'http/1.1',
workerStart: 0,
redirectStart: 0,
redirectEnd: 0,
fetchStart: 1149.1000000000931,
domainLookupStart: 1149.1000000000931,
domainLookupEnd: 1149.1000000000931,
connectStart: 1149.1000000000931,
connectEnd: 1149.1000000000931,
secureConnectionStart: 1149.1000000000931,
requestStart: 1149.6000000000931,
responseStart: 1244.3000000002794,
responseEnd: 1246,
transferSize: 21049,
encodedBodySize: 20749,
decodedBodySize: 20749,
serverTiming: [],
workerTiming: []
}
API Времени отрисовки(first-paint и first-contentful-paint)
API Paint Timing предоставляет информацию о так называемых первом цвете и первом значимом цвете, здесь имеется в виду скорость отрисовки крупнейшего видимого элемента страницы в поле зрения. Доступ к записям осуществляется через performance.getEntriesByType('paint') или performance.getEntriesByName('first-contentful-paint').
const { chromium } = require('playwright')
;(async () => {
const browser = await chromium.launch()
const page = await browser.newPage()
await page.goto('https://danube-web.shop/')
const paintTimingJson = await page.evaluate(() =>
JSON.stringify(window.performance.getEntriesByType('paint'))
)
const paintTiming = JSON.parse(paintTimingJson)
console.log(paintTiming)
await browser.close()
})()
[
{ name: 'first-paint', entryType: 'paint', startTime: 1149.5, duration: 0 },
{ name: 'first-contentful-paint', entryType: 'paint', startTime: 1149.5, duration: 0 }
]
API Contentful Paint(largest-contentful-paint)
API Largest Contentful Paint предоставляет информацию о метриках скорости загрузки контента. Используйте этот API, чтобы оценить метрику Largest Contentful Paint (LCP) в Core Web Vital.
Отрисовка Contentful Paint — это не одно событие, а скорее потоки событий. За элементом всегда может последовать другой, более значимый. Для оценки LCP-метрик инициализируйте PerformanceObserver, наблюдайте за largest-contentful-paint и фиксируйте эти метрики.
const { chromium } = require('playwright')
;(async () => {
const browser = await chromium.launch()
const page = await browser.newPage()
await page.goto('https://danube-web.shop/')
const largestContentfulPaint = await page.evaluate(() => {
return new Promise((resolve) => {
new PerformanceObserver((l) => {
const entries = l.getEntries()
// the last entry is the largest contentful paint
const largestPaintEntry = entries.at(-1)
resolve(largestPaintEntry.startTime)
}).observe({
type: 'largest-contentful-paint',
buffered: true
})
})
})
console.log(parseFloat(largestContentfulPaint)) // 1139.39
await browser.close()
})()
API Нестабильности разметки (layout-shift)
API нестабильности разметки предоставляет информацию обо всех сдвигах разметки (макета). Используйте этот API для оценки так называемого Общего сдвига разметки (CLS) в Core Web Vital.
Сдвиги разметки — не одно событие, а поток событий. Для вычисления CLS инициализируйте PerformanceObserver, наблюдайте за записями layout-shift и суммируйте все сдвиги.
const { chromium } = require('playwright')
;(async () => {
const browser = await chromium.launch()
const page = await browser.newPage()
await page.goto('https://danube-web.shop/')
const cummulativeLayoutShift = await page.evaluate(() => {
return new Promise((resolve) => {
let CLS = 0
new PerformanceObserver((l) => {
const entries = l.getEntries()
entries.forEach(entry => {
if (!entry.hadRecentInput) {
CLS += entry.value
}
})
resolve(CLS)
}).observe({
type: 'layout-shift',
buffered: true
})
})
})
console.log(parseFloat(cummulativeLayoutShift)) // 0.0001672498
await browser.close()
})()
Long Task API(longtask)
API Long Task предоставляет информацию обо всех выполнениях JavaScript, занимающих 50 миллисекунд и более. Используйте этот API для оценки метрики Total Blocking Time (TBT) в Web Vital.
«Длинные задачи» — это, как и в предыдущих примерах, не отдельные события, а потоки событий. Для расчета TBT инициализируйте PerformanceObserver, наблюдайте за записями longtasks и суммируйте события с временем выполнения JavaScript в 50 миллисекунд и более.
const { chromium } = require('playwright')
;(async () => {
const browser = await chromium.launch()
const page = await browser.newPage()
await page.goto('https://danube-web.shop/')
const totalBlockingTime = await page.evaluate(() => {
return new Promise((resolve) => {
let totalBlockingTime = 0
new PerformanceObserver(function (list) {
const perfEntries = list.getEntries()
for (const perfEntry of perfEntries) {
totalBlockingTime += perfEntry.duration - 50
}
resolve(totalBlockingTime)
}).observe({ type: 'longtask', buffered: true })
// Resolve promise if there haven't been long tasks
setTimeout(() => resolve(totalBlockingTime), 5000)
})
})
console.log(parseFloat(totalBlockingTime)) // 0
await browser.close()
})()
Инструменты производительности Chrome DevTools
Если API производительности браузера недостаточно, то протокол Chrome DevTools предлагает множество инструментов производительности, которые мы можем использовать в Playwright.
Одним из важных примеров является троттлинг сети, с помощью которого мы можем имитировать работу пользователей, заходящих на нашу страницу в различных сетевых условиях.
const { chromium } = require('playwright')
;(async () => {
const browser = await chromium.launch()
const page = await browser.newPage()
const client = await page.context().newCDPSession(page)
await client.send('Network.enable')
await client.send('Network.emulateNetworkConditions', {
offline: false,
downloadThroughput: (4 * 1024 * 1024) / 8,
uploadThroughput: (3 * 1024 * 1024) / 8,
latency: 20
})
await page.goto('https://danube-web.shop/')
// your flow and assertions
await browser.close()
})()
Протокол DevTools весьма обширен. Мы рекомендуем изучить его документацию, чтобы получить полное представление о его возможностях.
Дополнительные библиотеки производительности
Также можно использовать Lighthouse с Playwright, для сбора метрик, таких как Time To Interactive (TTI):
const chromeLauncher = require('chrome-launcher')
const { chromium } = require('playwright')
const lighthouse = require('lighthouse')
const request = require('request')
const util = require('util')
;(async () => {
const chrome = await chromeLauncher.launch()
const resp = await util.promisify(request)(
`http://localhost:${chrome.port}/json/version`
)
const { webSocketDebuggerUrl } = JSON.parse(resp.body)
const browser = await chromium.connect({ wsEndpoint: webSocketDebuggerUrl })
const { lhr } = await lighthouse(
'https://danube-web.shop/',
{ port: chrome.port },
null
)
console.log('Report complete for', lhr.finalUrl)
console.log(`
Time To Interactive - Score: ${lhr.audits.interactive.score},
Value: ${lhr.audits.interactive.numericValue} ${lhr.audits.interactive.numericUnit}
`)
await browser.close()
await chrome.kill()
})()
Все приведенные выше примеры можно выполнить следующим образом:
$ node measure-performance.js
Перевод статьи «Measuring Page Performance Using Playwright — Best Practices».
Пингбэк: Большой учебник по Playwright