Wenn UI-Tests auf einem Cloud-Mac fehlschlagen, reichen Assertions und Screenshots allein oft nicht aus. Ein Dialog, der sofort wieder verschwindet, ein mitten im Übergang hängen gebliebener Bildschirm oder eine Tastatur, die eine Schaltfläche verdeckt: Nur eine fortlaufende Aufzeichnung zeigt, in welcher Reihenfolge diese Ereignisse aufgetreten sind. Statt sämtliche Videos dauerhaft aufzubewahren, sollte die Aufzeichnung des vorgesehenen Simulators vor dem Test gestartet werden. Bei Erfolg wird das Video gelöscht, bei einem Fehler zusammen mit den Protokollen und dem Ergebnis-Bundle archiviert.
Testgerät und Umfang der Diagnose festlegen
Übergeben Sie nicht einfach booted an den Aufnahmebefehl. Durch parallele Jobs, nach einem früheren Abbruch weiterlaufende Geräte oder manuelle Debugging-Sitzungen können mehrere Simulatoren gleichzeitig gestartet sein. Die Aufnahme könnte dann vom falschen Gerät stammen. Erstellen oder ermitteln Sie zuerst ein dediziertes Gerät für den aktuellen Job, lesen Sie dessen UDID aus und verwenden Sie diese anschließend für Start, Test, Aufnahme und Bereinigung.
DEVICE_NAME="CI-iPhone"
RUNTIME="com.apple.CoreSimulator.SimRuntime.iOS-18-0"
DEVICE_TYPE="com.apple.CoreSimulator.SimDeviceType.iPhone-16"
UDID="$(xcrun simctl list devices available -j |
plutil -extract devices json -o - - |
jq -r --arg name "$DEVICE_NAME" '
.. | objects | select(.name? == $name) | .udid
' | head -n 1)"
if [ -z "$UDID" ]; then
UDID="$(xcrun simctl create "$DEVICE_NAME" "$DEVICE_TYPE" "$RUNTIME")"
fi
xcrun simctl boot "$UDID" 2>/dev/null || true
xcrun simctl bootstatus "$UDID" -b
Die Runtime-Kennung hängt von der installierten Xcode-Version ab. Die Pipeline sollte sie daher vor der Ausführung mit xcrun simctl list runtimes überprüfen, statt auf allen Maschinen dieselbe Version vorauszusetzen. Bei parallel ausgeführten Jobs sollte außerdem die Jobnummer in den Gerätenamen aufgenommen werden, damit kein Zustand gemeinsam genutzt wird.
Die Aufnahme soll die zeitliche Abfolge eines Fehlers ergänzen, nicht die Protokolle ersetzen. Das Video beantwortet die Frage „Was geschah zuerst in der Benutzeroberfläche?“, während Protokolle und xcresult zeigen, was das Test-Framework aufgezeichnet hat.
Aufnahmeprozess zuverlässig beenden
simctl io recordVideo läuft dauerhaft weiter, weshalb das Skript seine PID speichern muss. Senden Sie nach Abschluss des Tests nicht sofort ein Signal zur erzwungenen Beendigung. Senden Sie zuerst INT, damit das Tool den Index des Video-Containers vollständig schreiben kann, und warten Sie anschließend auf das Ende des Prozesses.
ARTIFACTS="${PWD}/artifacts"
VIDEO="${ARTIFACTS}/ui-test.mp4"
RESULT="${ARTIFACTS}/UITests.xcresult"
LOG="${ARTIFACTS}/xcodebuild.log"
mkdir -p "$ARTIFACTS"
rm -rf "$RESULT"
rm -f "$VIDEO" "$LOG"
xcrun simctl io "$UDID" recordVideo \
--codec=h264 \
--force "$VIDEO" &
RECORDER_PID=$!
finish_recording() {
if kill -0 "$RECORDER_PID" 2>/dev/null; then
kill -INT "$RECORDER_PID"
wait "$RECORDER_PID" || true
fi
}
trap finish_recording EXIT INT TERM
H.264 lässt sich in der Regel problemlos im Browser oder als Anhang eines Tickets wiedergeben. Wenn das Team später Einzelbilder analysieren muss, kann nach Prüfung der Kompatibilität mit dem Artefaktsystem ein anderer Codec verwendet werden. Innerhalb derselben Pipeline sollten jedoch keine Formate gemischt werden, da Wiedergabewerkzeuge und Aufbewahrungsregeln sonst unvorhersehbar werden.
Tatsächlichen Exit-Code des Tests beibehalten
Die Aufnahme ist ein begleitender Prozess und darf das Ergebnis von xcodebuild nicht überdecken. Am zuverlässigsten ist es, die sofortige Beendigung bei Fehlern vorübergehend zu deaktivieren, den Exit-Code des Tests separat zu speichern und die Aufnahme anschließend regulär zu beenden. Soll das Protokoll zusätzlich über tee ausgegeben werden, muss der Exit-Status von xcodebuild innerhalb der Pipeline ausgewertet werden – nicht der erfolgreiche Status von tee.
set +e
xcodebuild test \
-workspace App.xcworkspace \
-scheme AppUITests \
-destination "platform=iOS Simulator,id=${UDID}" \
-resultBundlePath "$RESULT" \
>"$LOG" 2>&1
TEST_STATUS=$?
set -e
finish_recording
trap - EXIT INT TERM
if [ "$TEST_STATUS" -eq 0 ]; then
rm -f "$VIDEO"
exit 0
fi
exit "$TEST_STATUS"
Das Skript wird nach einem fehlgeschlagenen Test bewusst nicht sofort beendet, da die Aufnahme noch ordnungsgemäß abgeschlossen werden muss. Für resultBundlePath ist bei jedem Job ein eigener Pfad zu verwenden. Löschen Sie das alte Verzeichnis vor der Ausführung, damit frühere Ergebnisse nicht mit dem aktuellen Fehler vermischt werden.
Video prüfen, statt nur seine Existenz festzustellen
Dateien mit null Byte, kurze Dateien, die lediglich einen Container-Header enthalten, und gewaltsam abgebrochene Aufnahmen besitzen keinen diagnostischen Wert. Prüfen Sie vor der Archivierung mindestens die Dateigröße und lesen Sie die Laufzeit mit den Medienwerkzeugen des Systems aus. Scheitert ein Test unmittelbar nach dem Start, kann ein sehr kurzes Video normal sein. Der Mindestwert für die Laufzeit sollte deshalb nicht zu hoch angesetzt werden.
| Prüfkriterium | Empfohlene Maßnahme | Vorgehen bei einem Fehler |
|---|---|---|
| Datei vorhanden | test -f |
Protokoll aufbewahren und fehlende Aufnahme vermerken |
| Datei nicht leer | test -s |
PID des Aufnahmeprozesses und freien Speicherplatz prüfen |
| Laufzeit lesbar | Medienmetadaten abfragen | Als beschädigte Datei behandeln und keine leere Hülle hochladen |
| Gerät stimmt überein | Gespeicherte UDID vergleichen | Job abbrechen und falsches Gerät bereinigen |
| Testergebnis-Bundle | xcresult-Verzeichnis prüfen | Rohprotokoll als Rückfallebene aufbewahren |
UDID, Startzeit des Tests und Pfad der Aufnahme können in eine einfache Textdatei geschrieben und zusammen mit dem Video archiviert werden. Zugangsdaten, sämtliche Umgebungsvariablen oder nicht bereinigte Befehlszeilen gehören nicht in diese Datei. Auch das Video selbst kann Benachrichtigungen, Namen von Testkonten oder Geschäftsdaten enthalten. Test-Fixtures sollten deshalb ausschließlich dafür vorgesehene, nicht vertrauliche Beispieldaten verwenden.
Parallelität, Zeitüberschreitungen und verbliebene Prozesse behandeln
Jeder Job benötigt einen eigenen Simulator, ein eigenes Ergebnisverzeichnis und einen eigenen Dateinamen für die Aufnahme. Gemeinsam verwendete Ressourcen wie booted, ein festes artifacts/ui-test.mp4 oder ein öffentliches DerivedData-Verzeichnis führen dazu, dass parallele Jobs ihre Daten gegenseitig überschreiben. Das jobspezifische Verzeichnis kann anhand einer von der Pipeline bereitgestellten eindeutigen Nummer erzeugt werden. Temporäre Dateien erfolgreicher Jobs sollten beim Beenden gelöscht werden.
Wenn ein übergeordneter Timeout-Mechanismus den Test beendet, muss die Bereinigungsfunktion weiterhin ausgeführt werden können. Senden Sie dem Testprozess zunächst ein verarbeitbares Beendigungssignal, lassen Sie trap einige Sekunden Zeit, um die Aufnahme abzuschließen, und erzwingen Sie die Bereinigung erst danach. Vor dem nächsten Job kann geprüft werden, ob noch simctl io-Aufnahmeprozesse existieren, deren übergeordneter Prozess bereits beendet wurde. Verwenden Sie jedoch kein pauschales killall, da sonst andere Jobs auf derselben Maschine beendet werden könnten.
Ein zuverlässiger Archivsatz besteht üblicherweise nur aus drei Elementen: dem Video des fehlgeschlagenen Tests, dem xcresult-Ergebnis-Bundle und dem ursprünglichen Build-Protokoll. Alle drei sollten mit derselben Jobnummer benannt und gleich lange aufbewahrt werden. Nach dem Öffnen eines Jobs aus der Fehlerliste lässt sich so zuerst anhand des Videos der Zustand der Benutzeroberfläche feststellen. Danach können Assertions und Anhänge im Ergebnis-Bundle lokalisiert und schließlich Befehle, Zielgerät und Exit-Code im Protokoll überprüft werden. Auf diese Weise entsteht eine vollständige, aber nicht überladene Beweiskette.
Häufig gestellte Fragen
Warum sollte booted nicht als Simulatorziel verwendet werden?
Bei parallelen Jobs können mehrere Simulatoren laufen. Eine feste UDID stellt sicher, dass Start, Test, Aufnahme und Bereinigung dasselbe Gerät betreffen.
Warum lässt sich eine erzeugte Videodatei manchmal nicht abspielen?
Ein erzwungenes Beenden kann das Schreiben des Containerindex unterbrechen. Senden Sie INT und warten Sie auf das Ende des Aufnahmeprozesses.
Sollten Videos erfolgreicher Tests gespeichert werden?
Meistens nicht. Löschen Sie erfolgreiche Aufnahmen und archivieren Sie Video, Protokoll und xcresult nur nach einem fehlgeschlagenen Testlauf.
Den nächsten Build auf einem dedizierten physischen Apple-Silicon-Knoten ausführen
Wählen Sie Laufzeit und Knotenregion aus, prüfen Sie Mac Mini M4, 16 GB RAM, 256 GB SSD und den fälligen USD-Betrag und geben Sie anschließend die Bestellung auf.