WKWebView-Sitzungen auf dem Cloud-Mac sauber voneinander trennen

WKWebView-Sitzungen auf dem Cloud-Mac sauber voneinander trennen

Wenn ein Team iOS-Regressionstests auf einem Cloud-Mac ausführt, öffnet die App häufig nacheinander zwei Web-Sitzungen: erst meldet sie sich bei Workspace A an, dann wechselt sie zu Workspace B. Liest B dabei einen Cookie von A, kann die Oberfläche zunächst trotzdem normal laden. Der Fehler fällt womöglich erst beim Identitätswechsel auf. Statt nur zu prüfen, ob sich die Seite öffnen lässt, sollte die Grenze zwischen den Website-Datenspeichern als reproduzierbarer Test festgehalten werden.

Zuerst die Isolationsgrenze festlegen

WKWebView wählt den Website-Datenspeicher über WKWebViewConfiguration.websiteDataStore. WKWebsiteDataStore.default() steht für persistente Daten; jeder Aufruf von WKWebsiteDataStore.nonPersistent() kann eine neue temporäre Speicherinstanz erzeugen. Hier wird zunächst die Cookie-Isolation zwischen zwei unabhängigen temporären Speichern geprüft. Das Ergebnis ist kein Nachweis dafür, dass der gesamte Anmeldeablauf sicher ist.

Vor dem Schreiben des Tests sollten die Stellen im App-Code ermittelt werden, an denen Web-Views erzeugt werden. Halten Sie fest, welcher Speicher an welche View gebunden ist. Sollen mehrere Seiten derselben Identität eine Sitzung teilen, dürfen sie nicht versehentlich getrennte temporäre Speicher verwenden. Soll nach einem Identitätswechsel eine neue Sitzung beginnen, darf die neue View nicht weiter auf den alten Speicher verweisen. Eine Tabelle dazu, welche Seiten Daten teilen und welche getrennt bleiben, ist verlässlicher als später über die Herkunft eines Cookies zu rätseln.

WKProcessPool ist hier nicht das Abnahmekriterium. Aus einem gleichen oder unterschiedlichen Prozesspool allein lässt sich nicht schließen, ob Website-Daten geteilt oder isoliert sind. Prüfen Sie den tatsächlich gebundenen websiteDataStore.

Den Datenspeicher ohne externe Dienste testen

Importieren Sie WebKit und XCTest in das Unit-Test-Target der App. Der folgende Test greift ausschließlich auf den Cookie-Speicher zu und sendet keine Netzwerkanfragen. Er benötigt daher weder einen Testserver noch eine bestimmte Ladegeschwindigkeit der Seite. test.invalid ist die Domain des Beispiel-Cookies, keine Adresse für einen Dienst.

import WebKit
import XCTest

final class WebsiteDataIsolationTests: XCTestCase {
    func testEphemeralStoresDoNotShareCookie() {
        let storeA = WKWebsiteDataStore.nonPersistent()
        let storeB = WKWebsiteDataStore.nonPersistent()
        let done = expectation(description: "Both stores checked")

        guard let cookie = HTTPCookie(properties: [
            .domain: "test.invalid",
            .path: "/",
            .name: "isolation_probe",
            .value: "only_a"
        ]) else {
            XCTFail("Could not create probe cookie")
            return
        }

        storeA.httpCookieStore.setCookie(cookie) {
            storeA.httpCookieStore.getAllCookies { cookiesA in
                XCTAssertTrue(cookiesA.contains {
                    $0.name == "isolation_probe" && $0.value == "only_a"
                })

                storeB.httpCookieStore.getAllCookies { cookiesB in
                    XCTAssertFalse(cookiesB.contains {
                        $0.name == "isolation_probe"
                    })
                    done.fulfill()
                }
            }
        }

        wait(for: [done], timeout: 5)
    }
}

Prüfen Sie zuerst, ob A den gerade geschriebenen Wert lesen kann, und danach, ob B keinen gleichnamigen Cookie enthält. Diese Reihenfolge ist wichtig: Wird B vor Abschluss des Schreibvorgangs gelesen, könnte der Test selbst dann „bestehen“, wenn beide Speicher fälschlich Daten teilen. Ein Timeout bedeutet, dass die Callback-Kette nicht abgeschlossen wurde. Das muss als Fehler untersucht werden, statt die Wartezeit immer weiter zu erhöhen.

Den Speicher mit echten Seiten verbinden

Wenn der Test der Speicherebene bestanden ist, prüfen Sie, wie die App ihre Views erstellt. Die Konfiguration muss vor der Initialisierung von WKWebView festgelegt werden. Eine nachträgliche Änderung an einer anderen Konfiguration ersetzt nicht den Speicher einer bereits erstellten View. Im Testaufbau für die Seite lässt sich die gewünschte Instanz ausdrücklich übergeben:

let sessionStore = WKWebsiteDataStore.nonPersistent()
let configuration = WKWebViewConfiguration()
configuration.websiteDataStore = sessionStore
let webView = WKWebView(frame: .zero, configuration: configuration)

Der anschließende Integrationstest sollte eine kontrollierte Testseite laden. Lassen Sie die Seite Anmeldung, Statusabfrage, Identitätswechsel und Abmeldung durchführen und beobachten Sie dabei Anfragen und Seitenanzeige. Die Speicher-API kann nachweisen, dass der Test-Cookie in der jeweils anderen Instanz nicht sichtbar ist. Sie kann für sich genommen aber weder belegen, dass die serverseitige Sitzung beendet wurde, noch ausschließen, dass die App auf nativer Ebene erneut ein Token in die Seite einschleust. Sind echte Anfragen erforderlich, verwenden Sie einen vom Team selbst betriebenen, kontrollierten Dienst und erfassen Sie Netzwerkfehler getrennt von den Sitzungsprüfungen.

Persistenter Speicher braucht eigene Tests

Aus einem bestandenen Test mit nonPersistent() lässt sich nicht ableiten, dass die Abmeldung korrekt funktioniert. Verwendet die App default(), muss der Abmeldetest prüfen, ob die betreffenden Website-Daten im zugehörigen persistenten Speicher gemäß den Produktregeln behandelt werden. Erstellen Sie außerdem eine neue Web-View und testen Sie erneut. Grenzen Sie vor einer Bereinigung die zu löschenden Daten ein. Löschen Sie nicht pauschal Einträge, die andere Seiten auf dem Gerät noch benötigen könnten, nur damit der Test besteht.

Ergebnisse auf dem Cloud-Mac reproduzierbar halten

Führen Sie dasselbe Test-Target auf dem Entwicklungsrechner und in automatisierten Jobs aus und bewahren Sie die jeweiligen Ergebnisse getrennt auf. Legen Sie vor dem Lauf die vom Projekt geforderte Xcode-Version, die Systemversion des Simulators und die Testkonfiguration fest. Hier wird nicht vorausgesetzt, dass eine bestimmte Systemversion geeignet ist. Erstellen Sie den temporären Speicher in jeder Testmethode neu und verwenden Sie einen eigenen Namen für den Test-Cookie, damit Testfälle nicht unbemerkt gegenseitig ihren Zustand übernehmen.

Wenn ein Test einzeln besteht, aber im gesamten Testlauf fehlschlägt, prüfen Sie zuerst drei Punkte: Verwendet der Test unbemerkt default()? Nutzen beide Web-Views dieselbe Speicherinstanz? Folgen asynchrones Schreiben, Seitennavigation und Assertions der Reihenfolge der Abschluss-Callbacks? Überdecken Sie das Problem nicht als Erstes durch längere Wartezeiten. Bewahren Sie für die spätere Analyse den Namen des fehlgeschlagenen Testfalls, die verwendeten Xcode- und Simulatorversionen sowie die Testergebnisdatei auf. Geben Sie keine echten Sitzungswerte in Logs aus.

Checkliste vor der Übergabe

Prüfpunkt Kriterium
Speicherzuordnung Der websiteDataStore jeder Web-View entspricht der vorgesehenen Sitzung
Temporäre Isolation Der von A geschriebene Test-Cookie ist im unabhängigen Speicher B nicht sichtbar
Callback-Reihenfolge Erst nach Abschluss des Schreibvorgangs A lesen, zuletzt B
Abmeldung prüfen Persistente Daten und serverseitige Sitzung getrennt nach Produktregeln prüfen
Fehlernachweise Umgebung und Assertions dokumentieren, keine echten Zugangsdaten

Diese Tests eignen sich vor allem als Prüfung der Datengrenze: Zuerst weisen sie nach, dass lokale Daten nicht zwischen temporären Speichern fließen. Anschließend prüfen Tests auf Seitenebene den Identitätswechsel und die Abmeldung. Halten Sie beide Ergebnisse getrennt fest. So lässt sich bei einem Fehler gezielt untersuchen, ob die Speicherbindung, der asynchrone Ablauf oder die Sitzungslogik der App die Ursache ist.

Häufig gestellte Fragen

Löscht ein temporärer Datenspeicher persistente Cookies?

Nein. nonPersistent() bereinigt default() nicht. Beim Abmelden muss der tatsächlich genutzte persistente Speicher gesondert geprüft werden.

Reichen getrennte WKProcessPool-Instanzen für die Cookie-Isolation?

Darauf sollten Sie sich nicht verlassen. Prüfen Sie den WKWebsiteDataStore jeder Webansicht und bei Bedarf auch die tatsächlichen Seitenanfragen.

XCZone · Cloud-Mac

Sie suchen einen exklusiven Mac, auf den Sie remote zugreifen können?

Vergleichen Sie Ausstattung, Standorte und Abrechnungszeiträume von M4 Start, M4 Work und M4 Pro – und finden Sie den passenden Tarif für Ihre Aufgaben.

Cloud-Mac-Tarife ansehen