iOS-Bildschirmdrehung testen, ohne Eingaben zu verlieren

iOS-Bildschirmdrehung testen, ohne Eingaben zu verlieren

In einem Bearbeitungsformular funktioniert die Eingabe im Hochformat, doch nach dem Wechsel ins Querformat ist der Entwurf leer. Oder die Schaltfläche zum Speichern befindet sich weiterhin im View-Baum, wird aber von der Tastatur verdeckt. Ein einmaliger Build auf einem Cloud-Mac deckt solche Probleme nicht auf. Der UI-Test muss den Ablauf „Eingeben – Drehen – Speichern – zurück ins Hochformat“ tatsächlich durchlaufen. Anhand einer iOS-App mit einer Seite zum Bearbeiten von Entwürfen richten wir dafür einen reproduzierbaren Prüfablauf ein.

Festlegen, was nach der Drehung erhalten bleiben muss

Vergeben Sie vor dem Test stabile accessibilityIdentifier für das Eingabefeld, die Speicherschaltfläche und die Anzeige des Speicherergebnisses: draftEditor, saveDraft und savedStatus. Diese Kennungen gehören in den App-Code; Elemente sollten nicht über lokalisierte Schaltflächenbeschriftungen gefunden werden. Der Test prüft, dass ungespeicherte Eingaben bei der Drehung erhalten bleiben, die Speicherschaltfläche im Querformat betätigt werden kann, nach dem Speichern ein Ergebnis erscheint und dieses auch nach der Rückkehr ins Hochformat sichtbar ist.

exists besagt nur, dass ein Element noch im Accessibility-Baum vorhanden ist – nicht, dass es sich antippen lässt. Prüfen Sie bei Drehungstests zusätzlich isHittable und tippen Sie das Element tatsächlich einmal an.

Stellen Sie zuerst sicher, dass die App die zu testende Ausrichtung unterstützt. Ist das Querformat in der Konfiguration der unterstützten Geräteausrichtungen deaktiviert, darf die ausbleibende Drehung nicht als Layoutfehler gelten. Verwenden Sie für Tablets und Smartphones auch nicht ungeprüft dieselben Erwartungen: Spaltenlayouts, Tastaturgrößen und Safe Areas können sich unterscheiden.

Simulator und Ausführungsumgebung festlegen

Führen Sie in einer grafischen Sitzung auf dem Cloud-Mac von XcodeOps zunächst xcrun simctl list devices available aus. Wählen Sie einen Smartphone-Simulator unter einer installierten Runtime und notieren Sie seine UDID. Gleichen Sie Geräte in CI nicht nur anhand ihres Namens ab: Gleichnamige Geräte können zu unterschiedlichen Runtimes gehören. Übergeben Sie die UDID als Umgebungsvariable SIMULATOR_UDID an den Job und prüfen Sie vor dem Start erneut, ob das Gerät noch als verfügbar aufgeführt wird.

Halten Sie für dieselbe Baseline den ausgewählten Xcode-Pfad, die Simulator-UDID, die Testdaten der App und den Einstiegspunkt des Tests konstant. Es genügt, die Ausgaben von xcodebuild -version und xcrun simctl list devices available zusammen mit dem Job zu protokollieren. Mischen Sie keine Ergebnisse eines anderen Simulators in diese Baseline. UI-Tests benötigen außerdem eine nutzbare grafische Anmeldesitzung. Eine funktionierende SSH-Verbindung bedeutet nicht automatisch, dass der Testprozess das Simulatorfenster bedienen kann.

Einen UI-Test für den gesamten Hin- und Rückweg schreiben

Fügen Sie den folgenden Test dem UI Test target des Projekts hinzu. Das Beispiel setzt voraus, dass die Bearbeitungsseite beim Start direkt sichtbar ist und die App die drei genannten Kennungen bereitstellt. Falls zunächst zur Bearbeitungsseite navigiert werden muss, tun Sie das vor der Eingabe ausdrücklich, statt sich auf eine aus einem früheren Lauf verbliebene Ansicht zu verlassen.

import XCTest

final class RotationTests: XCTestCase {
    func testDraftSurvivesRotation() {
        let device = XCUIDevice.shared
        device.orientation = .portrait
        defer { device.orientation = .portrait }

        let app = XCUIApplication()
        app.launch()

        let editor = app.textViews["draftEditor"]
        XCTAssertTrue(editor.waitForExistence(timeout: 10))
        editor.tap()
        editor.typeText("Rotation check 42")

        device.orientation = .landscapeLeft
        XCTAssertTrue(
            NSPredicate(format: "value == %@", "Rotation check 42")
                .evaluate(with: editor)
        )

        let save = app.buttons["saveDraft"]
        XCTAssertTrue(save.waitForExistence(timeout: 10))
        XCTAssertTrue(save.isHittable)
        save.tap()

        let status = app.staticTexts["savedStatus"]
        XCTAssertTrue(status.waitForExistence(timeout: 10))

        device.orientation = .portrait
        XCTAssertTrue(status.waitForExistence(timeout: 10))
    }
}

Das defer soll möglichst dafür sorgen, dass der nächste Test wieder im Hochformat beginnt. Es ersetzt nicht das Festlegen des Ausgangszustands in jedem einzelnen Test. Wenn die App bei der Drehung die Bearbeitungsansicht neu aufbaut, lassen sich bestehende Elementabfragen in der Regel erneut auflösen. Speichern Sie aber keine aus einer Abfrage ermittelten Koordinaten oder Frames zwischen. Bei asynchronem Speichern reicht es nicht aus, nur auf das Erscheinen eines Hinweises zu warten: Idealerweise stellt die App einen eindeutigen Erfolgsstatus bereit, dessen Wert geprüft wird. „Wird gespeichert“ darf nicht als Abschluss gelten.

Über die Kommandozeile ausführen und Ergebnisse sichern

Legen Sie vor dem Testlauf im Projektverzeichnis den Projektpfad, ein freigegebenes Scheme und die zuvor notierte UDID fest. Der folgende Befehl bricht sofort ab, wenn eine erforderliche Variable leer ist, und schreibt die Testergebnisse in ein neues Ergebnisverzeichnis. Verwenden Sie bei einem erneuten Lauf einen anderen Verzeichnisnamen.

: "${PROJECT:?Set the Xcode project path}"
: "${SCHEME:?Set a shared test scheme}"
: "${SIMULATOR_UDID:?Set an available simulator UDID}"

xcodebuild test \
  -project "$PROJECT" \
  -scheme "$SCHEME" \
  -destination "platform=iOS Simulator,id=$SIMULATOR_UDID" \
  -only-testing:AppUITests/RotationTests/testDraftSurvivesRotation \
  -resultBundlePath "$PWD/rotation-run.xcresult"

AppUITests muss dem Namen des UI Test target im Projekt entsprechen. Bewahren Sie bei einem Fehlschlag die .xcresult-Datei auf. Ermitteln Sie anhand des fehlgeschlagenen Schritts, der Screenshots und des Testprotokolls, ob die Eingabe zurückgesetzt oder die Schaltfläche verdeckt wurde. Verlassen Sie sich nicht nur auf die letzte Zeile im Terminal. Soll ein Skript die Ergebnisse auslesen, prüfen Sie zunächst mit xcrun xcresulttool help, welche Unterbefehle die aktuelle Xcode-Version bereitstellt, und wählen Sie dann die passende strukturierte Ausgabe.

Fehler nach Symptomen eingrenzen, statt sofort Wartezeiten einzubauen

Symptom Zuerst prüfen Lösungsansatz
Der Entwurf ist nach dem Wechsel ins Querformat leer Wird die Ansicht bei der Drehung neu aufgebaut, und wo liegt der Zustand? Den Bearbeitungszustand so ablegen, dass er einen Neuaufbau der Ansicht übersteht
Die Schaltfläche ist vorhanden, aber nicht antippbar Tastatur, Overlays, Safe Areas und Scrollcontainer Constraints korrigieren oder eine eindeutige Möglichkeit zum Schließen der Tastatur bereitstellen
Der Fehler tritt nur in CI auf Grafische Sitzung, Simulator-Runtime und Testdaten Ausführungsziel festlegen und für einen einheitlichen Ausgangszustand des Jobs sorgen
Der Hinweis verschwindet nach der Rückkehr ins Hochformat Ist der Hinweis nur eine kurzlebige Animation? Stattdessen einen dauerhaften Speicherstatus prüfen oder die Daten erneut lesen

Fügen Sie bei einem Fehler im Querformat nicht sofort ein festes sleep hinzu. Gibt es tatsächlich einen Übergang in der Oberfläche, warten Sie darauf, dass ein bestimmtes Element einen überprüfbaren Zustand erreicht. Bleibt die Schaltfläche dauerhaft von der Tastatur verdeckt, wird sie auch durch längeres Warten nicht antippbar. Tritt ein Problem nur bei bestimmten Abmessungen auf, ergänzen Sie einen zweiten Simulator mit anderer Bildschirmgröße, statt alle Geräte in einen einzigen Test zu packen. Stabilisieren Sie zuerst einen vollständigen Hin- und Rückweg mit festem Ausführungsziel und erweitern Sie erst danach die Abdeckung. So bleiben Fehlerprotokolle vergleichbar.

Häufig gestellte Fragen

Reicht ein Simulator für Rotationstests?

Für Ansichtszustand und Bedienbarkeit ja. Sensorverhalten und tatsächliche Handhabung sollten Sie zusätzlich am Gerät prüfen.

Warum ist eine gefundene Schaltfläche nicht antippbar?

Prüfen Sie zunächst, ob Tastatur oder Overlay sie verdecken, danach Safe-Area-Constraints und die Neuerstellung der Ansicht.

Direkt auf exklusiven physischen Knoten loslegen

Richten Sie einen Cloud-Mac für Ihre nächste Aufgabe ein

Wählen Sie einen Mac mini passend zum Arbeitsspeicherbedarf oder Build-Aufkommen. Drei Modellklassen sind tage-, wochen-, monats- oder quartalsweise buchbar. Die aktuelle Verfügbarkeit der Knoten sehen Sie in Echtzeit in der Konsole.

Jetzt Cloud-Mac mieten