Derselbe iOS-Oberflächencode kann auf dem Entwicklungsrechner korrekt aussehen und nach dem Zusammenführen dennoch zu abgeschnittenen Schaltflächen, durch dynamische Schriftgrößen verdrängten Inhalten oder unpassenden Farben im Dunkelmodus führen. Eine visuelle Regressionsprüfung soll keine Unit-Tests ersetzen. Sie beantwortet unter festgelegten Renderingbedingungen eine konkrete Frage: Hat dieser Commit die Pixel verändert, die Nutzer tatsächlich sehen?
Ein Cloud-Mac eignet sich gut dafür, solche Prüfungen kontinuierlich auszuführen. Voraussetzung ist jedoch, dass auch der Simulatorzustand, die Testdaten und die Vergleichsregeln versioniert werden. Einige Screenshots zu speichern, die „richtig aussehen“, reicht für eine stabile Prüfschranke nicht aus.
Zuerst eine stabile Screenshot-Matrix definieren
Beginnen Sie nicht mit dem Anspruch, „alle Seiten abzudecken“. Wählen Sie zunächst besonders wichtige Ansichten aus, etwa die Startseite vor der Anmeldung, zentrale Listen, Detailseiten sowie Leer- und Fehlerzustände. Legen Sie anschließend für jede Ansicht die folgenden Dimensionen fest:
| Dimension | Empfohlener fester Wert | Vorgehen bei Änderungen |
|---|---|---|
| Gerät | Ein eindeutig festgelegtes Simulator-Modell | Eigenes Referenzverzeichnis anlegen |
| System | Eine bestimmte verfügbare Runtime | Referenzbilder erneut prüfen |
| Darstellung | Hellen und dunklen Modus getrennt ausführen | Nicht darstellungsübergreifend vergleichen |
| Sprache | Für jede Zielsprache eigene Screenshots | Sprachcode in den Dateinamen aufnehmen |
| Schriftgröße | Mit der Standardschriftgröße beginnen | Für große Schriftgrößen eine eigene Testgruppe anlegen |
| Daten | Lokale Fixtures oder eine Testschnittstelle | Keine zufälligen Inhalte verwenden |
Als Referenzpfad eignet sich Snapshots/<runtime>/<device>/<locale>/<appearance>/. Die Verzeichnisstruktur ist zwar etwas länger, zeigt bei einem Fehlschlag aber sofort, in welcher Umgebung der Vergleich stattgefunden hat. So werden Renderingunterschiede zwischen verschiedenen Systemversionen nicht fälschlich als Produktregression eingestuft.
Referenzbilder sind keine dauerhaft gültige Wahrheit. Sie bilden einen geprüften Oberflächenvertrag ab, und jede Aktualisierung sollte gemeinsam mit der zugehörigen Codeänderung überprüft werden.
Den Simulator festlegen, statt einen bestehenden Zustand wiederzuverwenden
Ein Simulator, der über längere Zeit manuell verwendet wird, enthält häufig zurückgebliebene Berechtigungsdialoge, Tastatureinstellungen, Caches und Testkonten. Zuverlässiger ist es, den visuellen Tests ein eigenes Gerät zuzuweisen und dieses vor jedem Durchlauf herunterzufahren, zu löschen und neu zu starten.
#!/usr/bin/env bash
set -euo pipefail
: "${DEVICE_UDID:?DEVICE_UDID is required}"
xcrun simctl shutdown "$DEVICE_UDID" 2>/dev/null || true
xcrun simctl erase "$DEVICE_UDID"
xcrun simctl boot "$DEVICE_UDID"
xcrun simctl bootstatus "$DEVICE_UDID" -b
xcrun simctl status_bar "$DEVICE_UDID" override \
--time 09:41 \
--batteryState charged \
--batteryLevel 100 \
--wifiBars 3 \
--cellularBars 4
rm -rf TestResults/Visual.xcresult
xcodebuild test \
-workspace Example.xcworkspace \
-scheme ExampleVisualTests \
-destination "id=$DEVICE_UDID" \
-resultBundlePath TestResults/Visual.xcresult
rm -rf TestResults/Attachments
xcrun xcresulttool export attachments \
--path TestResults/Visual.xcresult \
--output-path TestResults/Attachments
Verlassen Sie sich nicht auf das aktuell gestartete Gerät booted. Parallele Jobs können mehrere Simulatoren gleichzeitig starten. Nur eine explizit übergebene UDID stellt sicher, dass Statusleistenanpassung, Installation und Tests auf demselben Gerät ausgeführt werden.
Screenshots anhand eines beobachtbaren Zustands auslösen
Einer der häufigsten Fehler bei visuellen Tests besteht darin, nach dem Start der App pauschal zwei Sekunden zu warten. Je nach Rechnerauslastung oder Netzwerkbedingungen sind zwei Sekunden manchmal zu lang und manchmal nicht lang genug. XCUITest sollte stattdessen auf ein barrierefrei zugängliches Element warten, das signalisiert, dass die Seite vergleichsbereit ist.
func capture(_ name: String, readyIdentifier: String) {
let app = XCUIApplication()
app.launchArguments = ["-VisualTestMode", "1"]
app.launch()
let ready = app.descendants(matching: .any)[readyIdentifier]
XCTAssertTrue(ready.waitForExistence(timeout: 15))
let image = XCUIScreen.main.screenshot()
let attachment = XCTAttachment(screenshot: image)
attachment.name = name
attachment.lifetime = .keepAlways
add(attachment)
}
Der Testmodus sollte Karussells, Skeleton-Animationen und blinkende Cursor deaktivieren sowie ein festes Datum, einen festen Benutzernamen und unveränderliche Listendaten einspeisen. Festgelegt werden dabei die Eingaben; die zu testende Oberfläche darf nicht durch eine andere Implementierung ersetzt werden. Wenn eine Seite auf den Abschluss einer Anfrage angewiesen ist, sollte die Oberfläche einen stabilen Indikator für den abgeschlossenen Ladevorgang bereitstellen, statt die Wartezeit immer weiter zu verlängern.
Schriftarten und Animationen behandeln
Schriftarten müssen vom System stammen oder mit der App ausgeliefert werden. Sie dürfen nicht von einer einmaligen manuellen Installation abhängen. Animationen lassen sich über Startparameter deaktivieren; vor dem Screenshot muss außerdem sichergestellt werden, dass kein Bildlauf mehr stattfindet. Für Videos, Karten oder Timer mit fortlaufenden Änderungen sollten bevorzugt deterministische Test-Fixtures eingesetzt werden. Masken sind nur für sehr kleine Bereiche sinnvoll, die sich nicht kontrollieren lassen.
Nachvollziehbare Pixeltoleranzen festlegen
Unterschiedliche Bytes in PNG-Dateien bedeuten nicht zwangsläufig, dass sich auch das Bild unterscheidet. Ein direkter Vergleich mit cmp reagiert leicht auf Abweichungen in den Kodierungsinformationen. Der Vergleicher sollte beide Bilder zunächst mit identischen Abmessungen und als sRGB-Pixel dekodieren und anschließend die Kanalabweichungen Pixel für Pixel berechnen.
Es empfiehlt sich, zwei Bedingungen gleichzeitig anzuwenden:
- Eine maximale Toleranz pro Kanal, die beispielsweise geringe Abweichungen beim Antialiasing zulässt.
- Den Anteil der Pixel, die diese Toleranz überschreiten; er darf nur einen sehr kleinen Teil des Gesamtbilds ausmachen.
Eine ausschließlich gemittelte Abweichung kann schwerwiegende lokale Fehler verdecken: Eine verschwundene Schaltfläche nimmt möglicherweise nur einen kleinen Teil des gesamten Bildes ein. Der Vergleicher sollte außerdem ein hervorgehobenes Differenzbild erzeugen und die Anzahl der Pixel über dem Grenzwert, deren Begrenzungsrahmen und die verwendeten Schwellenwerte protokollieren. Für zentrale Bestätigungsseiten können strenge Regeln gelten. Seiten mit Schatten oder komplexen Farbverläufen lassen sich separat konfigurieren. Die globalen Grenzwerte sollten jedoch nicht ständig gelockert werden, nur damit die Prüfung wieder „grün“ wird.
Fehlschläge als überprüfbare Nachweise archivieren
Bei jedem Fehlschlag sollten mindestens das Referenzbild, das tatsächliche Bild, das Differenzbild und die Datei .xcresult archiviert werden. Zusätzlich sind Commit-Kennung, Xcode-Version, System-Runtime, Gerätemodell, Sprache und Darstellungsmodus zu erfassen. Dateinamen sollten sich aus Testsuite, Seitenzustand und Umgebung zusammensetzen, damit parallele Jobs ihre Ergebnisse nicht gegenseitig überschreiben.
Gehen Sie bei der Fehleranalyse in dieser Reihenfolge vor: Prüfen Sie zuerst, ob sich Abmessungen oder Runtime geändert haben. Kontrollieren Sie danach die Stabilität der Testdaten und untersuchen Sie anschließend den Begrenzungsrahmen der Unterschiede. Sind Abweichungen über den gesamten Bildschirm verteilt, liegt meist eine Abweichung bei Schriftarten, Darstellungsmodus oder Farbraum vor. Konzentrieren sie sich dagegen auf ein einzelnes Steuerelement, ist eine Layoutänderung wahrscheinlicher.
Auch die Aktualisierung der Referenzbilder sollte als eigener Prozess erfolgen: Kandidatenbilder erzeugen, die Unterschiede manuell prüfen und die Referenzen erst ersetzen, nachdem die beabsichtigte Änderung bestätigt wurde. Ein fehlgeschlagener Test darf seine Referenzbilder nicht automatisch überschreiben. Andernfalls würde der nächste Durchlauf eine echte Regression unmittelbar akzeptieren.
Eine fertige Prüfschranke sollte drei Eigenschaften besitzen: Die Umgebung lässt sich reproduzieren, die Grenzwerte sind nachvollziehbar und Fehlschläge können wiederholt werden. Erst dann sind Screenshot-Tests eine belastbare technische Prüfung und kein Bildarchiv, das gelegentlich manuell bereinigt werden muss.
Häufig gestellte Fragen
Soll jeder Pixel exakt mit dem Referenzbild übereinstimmen?
Meistens nicht. Sinnvoller sind ein Grenzwert pro Farbkanal und ein maximaler Anteil abweichender Pixel. Nur unvermeidbar dynamische Bereiche sollten kleinflächig maskiert werden.
Warum unterscheiden sich Screenshots desselben Commits zwischen Simulatoren?
Gerätemodell, Systemversion, Schrift, Sprache, Zeitzone, Skalierung und Statusleiste beeinflussen das Rendering. Referenzbilder müssen deshalb an eine feste Umgebung gebunden sein.
Welche Dateien gehören zu einem fehlgeschlagenen Prüflauf?
Referenzbild, aktuelles Bild, Differenzbild, Testergebnis-Bundle, Commit-Kennung sowie Simulator- und Systemversion sollten gemeinsam archiviert werden.
Bewährte Workflows auf einen dauerhaft verfügbaren Cloud-Mac verlagern
Wählen Sie je nach Einsatz BookaMac M4 oder BookaMac M4 Pro und prüfen Sie bei der Bestellung Region, Laufzeit und optionale Speichererweiterungen.