Sobald eine iOS-App Anmelde-Callbacks, Aktionsseiten, Bestelldetails und Ziele aus Benachrichtigungen über Deep-Links ansteuert, kann bereits ein einziges Routing-Refactoring dazu führen, dass ein Link nur noch auf der Startseite landet oder bei geöffneter App dieselbe Seite erneut auf den Navigationsstapel legt. Einige Links manuell anzutippen, belegt lediglich, dass sie zu diesem Zeitpunkt funktioniert haben. Kaltstarts, der Vordergrundzustand, codierte Parameter und ungültige Eingaben bleiben dabei unberücksichtigt. Robuster ist es, die Simulatorumgebung auf einem Cloud-Mac fest vorzugeben und das endgültige Routing-Ergebnis jedes Deep-Links für die Continuous-Integration-Pipeline auslesbar zu machen.
Links zuerst als überprüfbaren Vertrag definieren
URLs sollten nicht an verschiedenen Stellen im Testskript zusammengesetzt werden. Pflegen Sie stattdessen zunächst einen Routing-Katalog, der Eingabe, erwartete Route und App-Zustand eindeutig festlegt.
| Szenario | Eingabe | Erwartetes Ergebnis |
|---|---|---|
| Produktdetails | linklab://product/42 |
product/42 |
| Suchparameter | linklab://search?q=swift%20ui |
search?query=swift ui |
| Fehlende Kennung | linklab://product/ |
error/invalid-product |
| Unbekannter Pfad | linklab://unknown |
error/not-found |
Die erwarteten Werte im Katalog sollten der vom Router aufgelösten kanonischen Form entsprechen, nicht etwa der Beschriftung einer Schaltfläche. Änderungen an UI-Texten erzeugen so keine Fehlalarme, und der Test kann unmittelbar erkennen lassen, ob Pfad, Parameter oder Zustandswiederherstellung fehlerhaft sind.
Die Deep-Link-Prüfung stellt fest, ob eine Eingabe die richtige fachliche Route erreicht – nicht nur, ob das System eine URL akzeptiert hat. Ein erfolgreicher Befehl bedeutet noch nicht, dass die richtige Seite geöffnet wurde.
Für dieselbe Route sollten mindestens je ein Fall für den Kaltstart und für die bereits im Vordergrund laufende App vorhanden sein. Wenn eine Route eine Authentifizierung voraussetzt, sind außerdem der angemeldete und der nicht angemeldete Zustand getrennt zu prüfen. Den Zustand des Testkontos muss das Skript jedoch ausdrücklich herstellen; er darf nicht vom vorherigen Testfall übernommen werden.
Einen beobachtbaren Prüfpunkt in den Debug-Build einbauen
Über die Kommandozeile lässt sich ein Link öffnen, aber nicht zuverlässig feststellen, welchen Inhalt die App letztlich anzeigt. Im DEBUG-Build kann der gemeinsame Routing-Einstieg deshalb das normalisierte Ergebnis in das Cache-Verzeichnis schreiben. Der Release-Build darf diese Logik nicht enthalten. Tokens oder vollständige Abfrageparameter dürfen ebenfalls nicht protokolliert werden.
#if DEBUG
func recordResolvedRoute(_ route: String) {
let payload: NSDictionary = [
"route": route,
"recordedAt": Date().timeIntervalSince1970
]
let directory = FileManager.default.urls(
for: .cachesDirectory,
in: .userDomainMask
)[0]
let file = directory.appendingPathComponent("link-probe.plist")
payload.write(to: file, atomically: true)
}
#endif
Der Prüfpunkt sollte hinter dem Router liegen, den URL Scheme, Universal Link und Sprünge aus Benachrichtigungen gemeinsam durchlaufen. Wird stattdessen separat in den jeweiligen Lifecycle-Callbacks protokolliert, weist der Test möglicherweise nur nach, dass ein Callback ausgelöst wurde. Er belegt dann nicht, dass die Parameter validiert wurden und die vorgesehene fachliche Seite tatsächlich erreicht wurde.
Ergebnisse atomar halten
Löschen Sie vor jedem Durchlauf die alte Datei und verwenden Sie beim Schreiben eine atomare Ersetzung. Andernfalls kann ein App-Absturz oder ein zu früher Lesezugriff dazu führen, dass der aktuelle Test irrtümlich das Ergebnis des vorherigen Durchlaufs auswertet. Protokolliert werden sollten nur Routenschlüssel, Fehlertyp und Zeitstempel; Benutzereingaben sind zuvor zu anonymisieren.
Simulator festlegen und die Routing-Matrix ausführen
Verwenden Sie in CI nicht das mehrdeutige booted als Ausgangspunkt. Ermitteln Sie zunächst anhand von Gerätename und Systemversion die UDID, stellen Sie sicher, dass ausschließlich der Zielsimulator gestartet ist, und warten Sie anschließend auf den vollständigen Systemstart. Das folgende Beispiel setzt voraus, dass die Pipeline SIMULATOR_UDID bereits ermittelt hat.
set -euo pipefail
xcrun simctl shutdown all
xcrun simctl boot "$SIMULATOR_UDID"
xcrun simctl bootstatus "$SIMULATOR_UDID" -b
xcodebuild \
-scheme LinkLab \
-configuration Debug \
-sdk iphonesimulator \
-derivedDataPath .ci/DerivedData \
build
APP_PATH=".ci/DerivedData/Build/Products/Debug-iphonesimulator/LinkLab.app"
xcrun simctl install "$SIMULATOR_UDID" "$APP_PATH"
Ermitteln Sie nach der Installation zunächst den Datencontainer. Bereinigen Sie anschließend für jeden Testfall den Prüfpunkt, öffnen Sie den Link und fragen Sie das Ergebnis wiederholt ab. Vermeiden Sie ein festes, langes sleep: Ändert sich die Startdauer der App, kostet es entweder unnötig Zeit oder endet trotzdem, bevor die Datei verfügbar ist.
BUNDLE_ID="com.example.linklab"
DATA_DIR="$(xcrun simctl get_app_container \
"$SIMULATOR_UDID" "$BUNDLE_ID" data)"
PROBE="$DATA_DIR/Library/Caches/link-probe.plist"
rm -f "$PROBE"
xcrun simctl openurl "$SIMULATOR_UDID" "linklab://product/42"
for attempt in 1 2 3 4 5 6 7 8 9 10; do
test -f "$PROBE" && break
sleep 1
done
test -f "$PROBE"
ACTUAL="$(/usr/libexec/PlistBuddy -c "Print :route" "$PROBE")"
test "$ACTUAL" = "product/42"
Für den Vordergrundfall senden Sie nach dem ersten Öffnen weitere URLs an die laufende App. Beim Kaltstartfall führen Sie zuvor simctl terminate aus. Speichern Sie für jeden fehlgeschlagenen Testfall Eingabe, Sollwert, Istwert und Testprotokoll. Archivieren Sie jedoch nicht den vollständigen App-Container, wenn er vertrauliche Informationen enthalten kann.
Universal Links auf zwei Ebenen abnehmen
URL Scheme eignet sich für eine stabile Prüfung des Routers, ersetzt aber keine End-to-End-Abnahme von Universal Links. Diese hängen zusätzlich von der Berechtigung für zugeordnete Domains, der über HTTPS bereitgestellten Website-Datei, dem Inhaltstyp, der App-Kennung und dem Systemcache ab.
Auf der ersten Ebene wird bei jedem Commit der gemeinsame Router direkt aufgerufen. Dadurch lassen sich Pfadnormalisierung, Prozentcodierung, doppelte Parameter, leere Werte und unbekannte Routen schnell abdecken. Auf der zweiten Ebene werden echte HTTPS-Links in einer kontrollierten Testdomain geöffnet. Nach einer Neuinstallation der App sind sowohl der Kaltstart als auch der Wechsel zur bereits laufenden App zu prüfen. Änderungen an der Zuordnungsdatei der Website müssen zwingend die zweite Ebene auslösen; ausschließlich Unit-Tests auszuführen reicht nicht aus.
Ein häufiger Fehler besteht darin, direkt nach einer Änderung der Zuordnungsdatei denselben Link wiederholt zu öffnen und ein altes Cache-Ergebnis der neuen Konfiguration zuzuschreiben. Halten Sie bei der Fehlersuche die Build-Nummer der App, die Systemversion, die Installationsreihenfolge und die Antwortheader der Testdomain fest. Weisen Sie zuerst nach, dass das System den Link an die App übergibt, und untersuchen Sie erst danach das fachliche Routing.
Fehlernachweise und Bereinigung in die Regressionstür integrieren
Eine zuverlässige Regressionstür muss nicht nur einen von null verschiedenen Status zurückgeben, sondern den Verantwortlichen auch eine schnelle Einordnung der Fehlerursache ermöglichen. Die Fehlerzusammenfassung sollte mindestens den Namen des Testfalls, den App-Zustand, den Eingabetyp, die erwartete Route, die tatsächliche Route und die Information enthalten, ob die Prüfdatei erzeugt wurde. Fehlt die Datei, sind zuerst Installation, Lifecycle-Einstieg und Linkdeklaration zu kontrollieren. Ist die Datei vorhanden, enthält aber die falsche Route, sollten Parser und Navigationszustand untersucht werden.
Die Checkliste vor dem Merge kann kurz bleiben:
- Simulatormodell und Systemversion sind durch die Pipeline fest vorgegeben;
- Kaltstart und Vordergrundzustand werden getrennt ausgeführt;
- vor jedem Testfall werden der App-Prozess oder der Zustand des Prüfpunkts bereinigt;
- für Sonderzeichen, leere Parameter und unbekannte Pfade existieren Negativfälle;
- Universal Links besitzen eine eigenständige HTTPS-Abnahme;
- Fehlerartefakte enthalten keine Zugangsdaten, Tokens oder Benutzerdaten;
- nach Abschluss des Jobs wird der Simulator heruntergefahren und das temporäre Build-Verzeichnis gelöscht.
Führen Sie abschließend xcrun simctl shutdown "$SIMULATOR_UDID" aus und lassen Sie .ci/DerivedData durch die Bereinigungsrichtlinie des Arbeitsbereichs entfernen. Erst das Zusammenspiel aus Routing-Vertrag, beobachtbarem Prüfpunkt und Zustandsisolation macht aus der Aussage „Der Link lässt sich öffnen“ eine technische Regressionstür, die Navigationsfehler zuverlässig blockieren kann.
Häufig gestellte Fragen
Validiert simctl openurl einen Universal Link vollständig?
Nein. Neben dem App-Routing müssen die HTTPS-Zuordnungsdatei, signierte Berechtigungen, TLS-Auslieferung und Systemcaches separat geprüft werden.
Warum sind Tests bei Kaltstart und geöffneter App erforderlich?
Beide Zustände können unterschiedliche Lebenszyklus-Callbacks verwenden. Nur gemeinsam decken sie verlorene Startrouten und doppelte Navigation auf.
Wählen Sie dedizierte physische Knoten für Builds, Tests und Experimente
Zwei M4-Konfigurationen sind an Knotenstandorten in Singapur, Tokio, Seoul und Hongkong verfügbar. Maßgeblich ist der bei der Bestellung angezeigte aktuelle Verfügbarkeitsstatus.