Тестирование производительности в Playwright

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

Эта статья дает базовое представление о тестировании производительности веб-сайтов в Playwright.

Содержание:

Скорость важна

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

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

БЕСПЛАТНО СКАЧАТЬ КНИГИ в телеграм канале Библиотека тестировщика

Конкурентное преимущество

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

  1. Pinterest сократил время ожидания пользователей и увеличил трафик и конверсию.
  2. Компания Zalando применила небольшие меры по улучшению времени загрузки и обнаружила прямую корреляцию с увеличением дохода за сессию.
  3. BBC обнаружила, что каждая лишняя секунда загрузки страницы приводила к тому, что 10 % пользователей покидали ее.

Измерение производительности

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

Для получения наиболее полной информации можно комбинировать несколько подходов:

  1. Мониторинг реальных пользователей позволяет понять, какую производительность испытывают конечные пользователи вашего сервиса.
  2. Синтетический мониторинг для проактивного сбора информации о производительности сервиса и обнаружения проблем до того, как с ними столкнутся пользователи.
  3. Собственно Тестирование производительности, чтобы избежать деградации производительности, на проде в первую очередь.
  4. Регулярные аудиты для получения оценки эффективности и предложений по ее улучшению, например, с помощью таких инструментов, как Google Lighthouse.

Метрики производительности Google’s Web Vitals

Поскольку Google стремится обеспечить более быстрый Интернет, метрики Web Vitals всегда должны быть в поле вашего зрения. Такие показатели, как Время до первого байта (TTFB), Общее время блокировки (TBT) или Первый значимый рисунок в контенте (FCP), являются хорошими индикаторами пользовательского опыта и заслуживают внимания.

Google рекомендует обратить внимание на три наиболее важных показателя: Скорость отрисовки самого большого элемента контента (LCP), Задержка до первого пользовательского ввода (FID) и Общий сдвиг разметки (CLS). Эти три показателя считаются основными метриками веб-страницы и дают хорошее представление о скорости ее загрузки, интерактивности и визуальной стабильности.

Но не все метрики Google Web Vitals подходят для синтетического мониторинга и тестирования производительности. Задержка до первого пользовательского ввода (FID) зависит от взаимодействия с пользователем, и ее лучше всего измерять с помощью мониторинга реальных пользователей. Используйте Общее время блокировки (TBT) в качестве метрики интерактивности при внутреннем тестировании в компании.

Оценка производительности веб-сайтов с помощью headless-инструментов

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

  1. Доступ к API-интерфейсам Web Performance.
  2. При тестировании на Chromium – доступ к протоколу Chrome DevTools для проверки трафика, эмуляции сети и пр.
  3. Совместимость с библиотеками производительности из экосистемы Node.js.

API-интерфейсы Web Performance

Современные браузеры поддерживают множество API-интерфейсов Web Performance и Web Vitals.

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».

Оставьте комментарий

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