Bei Zustandsautomaten für In-App-Käufe ist meist nicht der erfolgreiche Kauf am schwierigsten zu reproduzieren, sondern Abbrüche, ausstehende Vorgänge, Erstattungen und nicht abgeschlossene frühere Transaktionen. Werden diese Pfade vollständig einer externen Transaktionsumgebung überlassen, beeinflussen Netzwerkbedingungen, Produktkonfiguration und der Zustand des Testkontos die Ergebnisse. Zuverlässiger ist es, die Produkte zunächst auf einem Cloud Mac über eine StoreKit-Konfigurationsdatei festzulegen und anschließend die Transaktionsbedingungen mit SKTestSession zu steuern. So wird die Clientlogik zu einem reproduzierbaren Regressionstest.
Grenzen von Offline-Tests klar definieren
StoreKit-Konfigurationen eignen sich dazu, die Zuordnung von Produktkennungen, Übergänge zwischen Kaufzuständen, die Aktualisierung von Berechtigungen, Fehlermeldungen und den Abschluss von Transaktionen zu prüfen. Ein Zugriff auf externe Zahlungswege ist nicht erforderlich, und Fehler lassen sich leichter reproduzieren.
Sie können jedoch weder belegen, dass Produkte in der Produktionsumgebung korrekt konfiguriert sind, noch serverseitige Benachrichtigungen, die Validierung echter Belege oder den endgültigen Zahlungsdialog abdecken. Es empfiehlt sich, die Tests in drei Ebenen aufzuteilen:
| Ebene | Hauptziel | Ausführungshäufigkeit |
|---|---|---|
| Unit-Tests | Berechtigungsberechnung, Zustandszuordnung | Bei jedem Commit |
| StoreKit-Integrationstests | Kauf, Abbruch, Erstattung, nicht abgeschlossene Transaktionen | Bei jedem Merge |
| Abnahme in externer Umgebung | Produktkonfiguration, Benachrichtigungs- und Belegpfade | Vor der Veröffentlichung |
Offline-Tests verkürzen die Feedbackschleife. Sie dienen nicht dazu, sämtliche Transaktionsrisiken hinter einem grünen Häkchen zu verbergen.
Produkte und Testeinstieg festlegen
Erstellen Sie in Xcode eine StoreKit Configuration File und behalten Sie darin nur die für den Test benötigten Produkte. Die Produktkennungen müssen mit den Konstanten im Code übereinstimmen. Preise dienen ausschließlich UI-Tests; die Geschäftslogik darf Berechtigungen nicht anhand lokalisierter Preiszeichenfolgen bestimmen.
Fügen Sie die Konfigurationsdatei dem Test-Scheme hinzu und erstellen Sie einen separaten Testplan, beispielsweise StoreKitRegression.xctestplan. Verwenden Sie nicht das Scheme, das Entwickler für ihre tägliche Arbeit ausführen. Eine einzelne manuelle Änderung könnte sonst unbemerkt die CI-Bedingungen verändern.
Das Test-Target kann die Sitzung in setUpWithError() neu erstellen:
import StoreKitTest
import XCTest
final class PurchaseRegressionTests: XCTestCase {
private var session: SKTestSession!
override func setUpWithError() throws {
session = try SKTestSession(configurationFileNamed: "Products")
session.disableDialogs = true
session.clearTransactions()
session.failTransactionsEnabled = false
}
override func tearDownWithError() throws {
session.clearTransactions()
session = nil
}
}
Der Name der Konfigurationsdatei wird ohne Dateierweiterung angegeben. Schlägt die Initialisierung fehl, prüfen Sie zuerst, ob die Datei dem Test-Target angehört und ob das Scheme dieselbe Konfiguration verwendet.
Transaktionspfade in separate Testfälle aufteilen
Testen Sie Erfolg, Erstattung und Abbruch nicht nacheinander in einem langen Testfall. Je mehr Zustände beteiligt sind, desto schwieriger lässt sich nach einem Fehler feststellen, in welchem Schritt der Zustand verunreinigt wurde. Mindestens die folgenden Fälle sollten getrennt werden:
Regulärer Kauf und Wiederherstellung
Prüfen Sie, ob die Berechtigungen unmittelbar nach dem Kauf aktualisiert werden und auch nach dem erneuten Erstellen der Geschäftslogikobjekte weiterhin als gültig gelesen werden können. Stellen Sie vor dem Testende sicher, dass die App die Transaktion verarbeitet und abgeschlossen hat. Andernfalls könnte der nächste Testfall erneut ein altes Update erhalten.
Abbruch und Fehlerinjektion
Ein Abbruch darf weder als „Zahlung fehlgeschlagen“ angezeigt werden noch einen entsperrten Zustand speichern. Bei Netzwerkfehlern oder allgemeinen Transaktionsfehlern muss hingegen eine Wiederholungsmöglichkeit bestehen bleiben. Nachdem ein Fehler über Sitzungseigenschaften injiziert wurde, müssen diese noch vor Ende des aktuellen Tests auf ihre Standardwerte zurückgesetzt werden. Verlassen Sie sich dabei nicht auf die Bereinigung durch den nächsten Testfall.
Erstattung und nicht abgeschlossene Transaktionen
Nach einer Erstattung muss die zugehörige Berechtigung entzogen werden, ohne andere weiterhin gültige Produkte versehentlich zu entfernen. Bei nicht abgeschlossenen Transaktionen sollte geprüft werden, ob die App die Verarbeitung nach einem Neustart fortsetzen kann. Es genügt nicht, lediglich einmalig einen Button-Callback zu kontrollieren.
Tests, die den Zustand desselben Produkts betreffen, sollten möglichst seriell ausgeführt werden. Teilen sich parallele Tests einen Simulator und dieselbe Konfiguration, können sie gegenseitig ihre Transaktionen löschen. Dadurch entstehen sporadische Fehler, die ausschließlich in der CI auftreten.
Ausführungsbedingungen auf der Kommandozeile festlegen
Prüfen Sie zunächst die verfügbaren Simulatoren und hinterlegen Sie anschließend Gerätename, Systemversion und Xcode-Pfad als CI-Variablen. Überlassen Sie xcodebuild nicht die automatische Auswahl eines beliebigen Ziels.
set -euo pipefail
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
RESULT_DIR="$PWD/TestResults"
rm -rf "$RESULT_DIR"
mkdir -p "$RESULT_DIR"
xcodebuild test \
-workspace App.xcworkspace \
-scheme App-StoreKitTests \
-testPlan StoreKitRegression \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-resultBundlePath "$RESULT_DIR/StoreKit.xcresult"
Wenn auf OakVM mehrere Pipelines gleichzeitig laufen, sollte jeder Auftrag ein eigenes Arbeitsverzeichnis und einen eigenen Simulator erhalten. DerivedData, Ergebnisverzeichnisse und bereits gestartete Geräte dürfen nicht gemeinsam verwendet werden. Vor dem Start kann xcrun simctl shutdown all ausgeführt werden. Da der Befehl jedoch auch andere Aufträge auf demselben Knoten beeinflusst, eignet er sich nur für Ausführungsmodelle, bei denen ein einzelner Auftrag den Knoten exklusiv nutzt.
Nachweise archivieren und Fehlerschwellen definieren
Nach einem fehlgeschlagenen Test reicht es meist nicht aus, nur die letzten Dutzend Zeilen der Konsolenausgabe zu speichern. Archiviert werden sollten xcresult, der Commit-Hash, die Prüfsumme der StoreKit-Konfigurationsdatei, die Xcode-Version und die Version der Simulator-Laufzeit. Änderungen an der Konfigurationsdatei müssen ebenfalls in die Codeüberprüfung einfließen, damit Produktkennungen nicht versehentlich entfernt werden.
Zunächst kann eine Zusammenfassung der Umgebung aufgezeichnet werden:
xcodebuild -version
xcrun simctl list runtimes
shasum -a 256 Tests/StoreKit/Products.storekit
Die Prüfliste sollte folgende Punkte enthalten:
- Erstellt und bereinigt jeder Test seine eigene
SKTestSession? - Sind automatische Dialoge deaktiviert, damit unbeaufsichtigte Aufträge nicht blockiert werden?
- Werden bei Abbruch, Fehler und Erstattung sowohl UI-Zustand als auch Berechtigungszustand separat geprüft?
- Hängt ein Test von der Ausführungsreihenfolge oder einer vorherigen Transaktion ab?
- Wird
xcresultauch nach einem Fehler hochgeladen? - Vermeiden die Protokolle vertrauliche Zugangsdaten und vollständige Transaktionsdaten?
Sind diese Bedingungen festgelegt, hängen Regressionstests für In-App-Käufe nicht mehr von manuellen Klicks ab. Die externe Umgebung übernimmt nur noch die Prüfungen, die tatsächlich dort stattfinden müssen. Bei alltäglichen Commits schützen schnelle, isolierte und nachvollziehbare Tests den Zustandsautomaten des Clients.
Häufig gestellte Fragen
Ersetzen Offline-Tests mit StoreKit die Abnahme in einer realen Testumgebung?
Nein. Sie prüfen den clientseitigen Zustandsautomaten und Fehlerpfade. Produktkonfiguration, Serverbenachrichtigungen, Belegverarbeitung und die echte Zahlungsoberfläche benötigen eine separate Abnahme.
Warum schlagen Kauftests nur innerhalb der gesamten Testsuite sporadisch fehl?
Häufig bleiben Transaktionen aus vorherigen Tests erhalten. Jede Testmethode sollte eine neue SKTestSession verwenden, Transaktionen löschen und Tests mit gemeinsamem Produktzustand seriell ausführen.
Welche Belege sollte CI bei einem Fehler archivieren?
Mindestens xcresult, Testprotokoll, Version der StoreKit-Konfiguration, Commit-Hash, Xcode-Version und Simulator-Laufzeit gehören zusammen in das Fehlerartefakt.
Den nächsten Build auf einem dedizierten physischen Knoten ausführen
Wählen Sie OakVM M4 oder OakVM M4 Pro und mieten Sie einen Cloud-Mac ohne Virtualisierung – tage-, wochen-, monats- oder quartalsweise. Der aktuelle Verfügbarkeitsstatus des Knotens wird in Echtzeit von der Konsole angezeigt.