×
Google Lighthouse Logo

Automatisiertes Testing mit Google Lighthouse



Eine gute Webseiten-Performance sichert zufriedene Nutzer - und die merken im Idealfall nicht mal etwas davon. Egal ob über ein mobiles Endgerät oder den PC, ein langsames Aufbauen einer Seite hinterlässt einen schwachen Eindruck. Und dennoch sind Webseiten, die nahezu instantan aufgebaut sind nicht die Regel. Optimierungspunkte gibt es viele und diese zu finden ist - mit Unterstützung von Tools - auch gar nicht so schwer. Dieser Artikel stellt den Beginn einer Reihe über Performance-Tests mit Google Lighthouse dar. Im ersten Teil werden grundlegende Funktionalität und Datenpunkte besprochen, die durch Lighthouse analysiert werden können.

Page Experience und Core Web Vitals

Mit den Core Web Vitals [1] hat Google einen Katalog an Kriterien herausgearbeitet, mit dem sich die Leistung einer Webseite in einigen prägnanten Kennzahlen, die für jede Art von Webseite und Webapp relevant sind, messen lässt. Insbesondere Wartezeiten für den Nutzer, wie die Zeit bis zur ersten Interaktionsmöglichkeit mit der Seite, sind hier zentral. Die von Google vorangetriebene Initiative, um Seitenperformance standardisiert messbar zu machen, nimmt in der Folge auch im Hinblick auf die Sichtbarkeit einer Webseite eine sehr wichtige Rolle ein. Die Bewertung anhand dieser Standards fließt ab Mai 2021 [2] unmittelbar in die PageRank-Bewertung von Google mit ein. Eine Seite mit einer niedrigen Performance wird also von Google entsprechend abgewertet und in Konsequenz gegenüber anderen, inhaltlich gleichwertigen Seiten weniger priorisiert bei Suchergebnissen angezeigt. Ob es weitere Kennzeichnungen und Konsequenzen für besonders gut oder besonders schlecht abschneidende Seiten geben wird, ist zum Zeitpunkt des Verfassens noch nicht bestätigt, sicherlich aber sehr gut denkbar.

Performance Scoring

Google Lighthouse ist ein Tool, mit dem Webseiten nach verschiedenen Gesichtspunkten auf den Prüfstand gestellt werden können. Dazu gehören unter anderem die Seitengeschwindigkeit (performance), Barrierefreiheit (accessibility), Umsetzung von Best Practices und SEO. Lighthouse gibt einer Webseite dann in jeder Kategorie sowie ihren Subkategorien jeweils eine Punktzahl entsprechend ihrem Abschneiden. Dazu gibt es noch eine Menge an Verbesserungsvorschlägen zu den Kriterien, in der die Webseite nicht gut abgeschnitten hat und bei denen noch Handlungsbedarf besteht. Ausgeführt werden kann Lighthouse sehr leicht ohne weitere Konfiguration auf der aktuell im Browser geöffneten Seite aus den Chrome Developer Tools heraus. Die Seitengeschwindigkeit einer öffentlichen Webseite kann auch url-basiert über die Web-UI unter Pagespeed Insights [3] getestet werden.

Lighthouse Scores
Abb. 1: Beispiel für ein Lighthouse Scoring.

Lighthouse im Testskript

Lighthouse kann auch programmatisch innerhalb von Testskripten benutzt werden. Dazu installiert man Lighthouse, beispielsweise über npm mittels

1
npm install lighthouse --save-dev

Lighthouse kann dann importiert und im Testskript konfiguriert werden. Im Beispiel wird ein headless Chrome gestartet und in diesem Lighthouse auf einer übergebenen URL ausgeführt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const lighthouse = require('lighthouse')
const chromeLauncher = require('chrome-launcher');
let runLighthouseOnUrl = url => {
  return chromeLauncher
    .launch({chromeflags: ['--headless']})
    .then(chrome => {
      const options = {
        port: chrome.port,
        output: 'json',
        logLevel: 'info',
      };
      return lighthouse(url, options).then(
        results => {
          return chrome.kill().then(() => {
            return {
              'js': results.lhr,
              'json': results.report
            }
          })
        }
      )
    })
  }

Da die Daten des generierten Reports nach der Konfiguration des Beispiels dann in einem Objekt und als JSON zur Verfügung stehen, können diese nun selbst nach den eigenen Wünschen ausgewertet werden. Die Daten enthalten sowohl Rohdaten, wie beispielsweise Ladezeiten von Assets, als auch die Punktzahlen, die Lighthouse diesen zuordnet. Demnach können also auch abseits der Bewertung von Lighthouse eigene Metriken konfiguriert werden und die Daten nach den eigenen Interessen ausgewertet werden.

Der Report

Der Report wird in der Konfiguration als Objekt und als JSON String generiert. Im Folgenden gibt es einen Einblick in einige interessante Teile des Reports.

Das audit-Objekt

Im audit-Objekt sind, wie der Name nahelegt, alle Audits, die Lighthouse durchführt, zu finden. Als Beispiel betrachten wir einmal das Audit “First Meaningful Paint”. Dieses prüft die benötigte Zeit, um den ersten sichtbaren Inhalt vollständig anzuzeigen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
"first-meaningful-paint": {
  "id": "first-meaningful-paint",
  "title": "First Meaningful Paint",
  "description": "first meaningful paint measures when the primary content of a page is visible. [learn more](https://web.dev/first-meaningful-paint/).",
  "score": 0.25,
  "scoreDisplayMode": "numeric",
  "numericValue": 5281.0819999999985,
  "numericUnit": "millisecond",
  "displayValue": "5.3 s"
}

Jedes Audit hat seine ID als Key. Das Objekt enthält den Klarnamen des Audits (“title”) sowie die Beschreibung, die beispielsweise im HTML-Report angezeigt wird. Das Feld “score” enthält die von Lighthouse ermittelte Bewertung. Abhängig von der Art des Audits, in diesem Beispiel eine Zeitdauer, gibt es Unterschiede, wie die ermittelten Werte dargestellt werden. Hier handelt es sich um einen numerischen Wert, der im Feld “numericValue” zu finden ist. Die Einheit, in der diese Zeitangabe ist, steht im Feld “numericUnit”. Die Art und Weise, wie Lighthouse den Wert aufbereitet, ist in dem Wert von “scoreDisplayMode” definiert. Bei “numeric” werden Zahlenwerte dargestellt, eine andere Möglichkeit wäre z.B. “binary”, welche einen Wahrheitswert darstellt. Dieser Modus kommt unter anderem im Audit “errors-in-console” vor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
"errors-in-console": {
  "id": "errors-in-console",
  "title": "No browser errors logged to the console",
  "description": "Errors logged to the console indicate unresolved problems. They can come from network request failures and other browser concerns. [learn more](https://web.dev/errors-in-console/)",
  "score": 1,
  "scoredisplaymode": "binary",
  "details": {
  "type": "table",
  "headings": [],
  "items": []
  }
}

Das categories-Objekt

Im audit-Objekt sind ausnahmslos alle durchgeführten Audits enthalten, diese sind aber nicht in die Kategorien “performance”, “accessibility”, etc. unterteilt. Der Report enthält neben dem audit-Objekt auch das categories-Objekt. Dieses enthält je wieder ein Objekt für die oben angesprochenen Kategorien. Das Beispiel zeigt einen Auschnitt aus dem performance-Objekt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
"performance": {
  "title": "performance",
  "auditRefs": [
    {
      "id": "first-contentful-paint",
      "weight": 15,
      "group": "metrics"
    },
    {
      "id": "speed-index",
      "weight": 15,
      "group": "metrics"
    },
    {
      "id": "largest-contentful-paint",
      "weight": 25,
      "group": "metrics"
    },
    {
      "id": "interactive",
      "weight": 15,
      "group": "metrics"
    },
// ... hier des platzes wegen abgekürzt
  ],
  "id": "performance",
  "score": 0.22
}

Die Testsuite bauen

Das Objekt enthält eine Gesamtbewertung (“score”). Alle Audits, die in dieser Kategorie herangezogen wurden, finden sich in dem Array “auditrefs”. Dort sind Objekte mit den IDs der Audits zu finden. Anhand all dieser beschriebenen Daten im Report kann man nun seine eigenen Auswertungen im Skript anstellen bzw. die Daten nach belieben aggregieren und weiterverarbeiten, um die gewünschte Analyse zu erzielen. Ein einfaches Beispiel könnte sein, bei den konkreten Zeiten zu prüfen, ob diese eine gesetzte maximale Zeit überschreiten. Den Ideen sind hier keine Grenzen gesetzt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import 'mocha'
import { expect } from 'chai'

describe('performance test suite', function () {
  let websiteUnderTest = 'yourwebsite.com'
  let lighthouseResults
  let lighthouseJson

    before(() => {
        return runLighthouseOnUrl(websiteUnderTest).then(r => {
            lighthouseResults = r.js
            lighthouseJson = r.json
        })
    })

    describe('Timings', () => {
        it('first meaningful paint', () => {
            expect(lighthouseResults.audits['first-meaningful-paint'].numericValue).to.be.lessThan(2000)
        })
    })
})

Fazit

Mit Lighthouse als Tool kann sehr schnell die Leistung einer Seite geprüft werden. Dabei bekommt man effektive Vorschläge zu möglichen Verbesserungen. Eingebunden in eine Testinfrastruktur lassen sich gezielte Analysen automatisiert durchführen und durchgehend beobachten. Dieser Artikel stellt den Beginn einer Reihe über die Integration und die Möglichkeiten von Google Lighthouse/ Web Vitals dar. In den folgenden Artikeln wird es um weiterführende Techniken gehen, wie diese Tools und Metriken innerhalb der eigenen Infastruktur automatisiert genutzt werden können, um Lighthouse CI und um Integrationen wie z.B. für Jenkins.

Komplettes Testskript

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import 'mocha'
import { expect } from 'chai'
const lighthouse = require('lighthouse')
const chromeLauncher = require('chrome-launcher');
const fs = require('fs');

let runLighthouseOnUrl = url => {
  return chromeLauncher
    .launch({chromeFlags: ['--headless']})
    .then(chrome => {
      const opts = {
        port: chrome.port,
        output: 'json',
        logLevel: 'info',
      };
      return lighthouse(url, opts).then(
        results => {
          return chrome.kill().then(() => {
            return {
              'js': results.lhr,
              'json': results.report
            }
          })
        }
      )
    })
}

describe('Performance Test Suite', function () {
    let websiteUnderTest = 'yourwebsite.com'
    let lighthouseResults
    let lighthouseJSON
    before(() => {
        return runLighthouseOnUrl(websiteUnderTest).then(r => {
            lighthouseResults = r.js
            lighthouseJSON = r.json
        })
    })
    describe('Timings', () => {
        it('First Meaningful Paint', () => {
            expect(lighthouseResults.audits['first-meaningful-paint'].numericValue).to.be.lessThan(2000)
        })
    })
})

Referenzen