Ein Matrix-Workflow kann mehrere iOS-Testjobs erzeugen. Nach der offiziellen Beschreibung des GitHub-Actions-Modells laufen diese Jobs aber nur dann wirklich gleichzeitig, wenn passende freie Runner verfügbar sind. Daraus folgt die wichtigste Entscheidung für die Aufteilung von iOS-UI-Tests in GitHub Actions:
Symptom: Ihre Matrix enthält mehrere Jobs, doch die Tests warten auf einem einzigen Mac weiterhin nacheinander.
Schnellste Lösung: Teilen Sie die UI-Suite zuerst mit Xcode Test Plans oder only-testing an stabilen Abhängigkeitsgrenzen. Routen Sie die Segmente danach per Matrix auf mehrere getrennte Remote-Mac-Runner. Mit nur einem Runner entsteht keine echte Parallelität über mehrere Knoten. Eine lokale Parallelisierung eignet sich erst nach bestätigter Isolation; für Release-Tests bleibt zunächst ein serieller oder dualer Kontrolllauf erhalten.
Diese Anleitung richtet sich an iOS-Testingenieure, die wachsende XCTest-UI-Suiten unabhängig ausführbar und leichter diagnostizierbar machen möchten. Sie unterstützt DevOps-Ingenieure beim Aufbau von Matrix-Jobs, Runner-Labels, Concurrency-Regeln und Artefakt-Uploads. Verantwortliche für die Entwicklungsplattform erhalten Kriterien dafür, ob zusätzliche Runner, bessere Testgrenzen oder eine Fehlerbereinigung den größten Effekt versprechen.
Warum mehrere Matrix-Jobs noch keine schnellere Testpipeline ergeben
Bei iOS-Tests werden häufig drei verschiedene Parallelitätsebenen verwechselt:
- GitHub-Actions-Job-Parallelität: Die Matrix erzeugt mehrere getrennte Jobs.
- Parallelität mehrerer Remote-Mac-Runner: Verschiedene Jobs werden auf unterschiedlichen macOS-Knoten ausgeführt.
- Xcode- und Simulator-Parallelität: Ein einzelner Mac startet mehrere Testprozesse oder Simulatorziele.
Diese Ebenen haben unterschiedliche Engpässe. Eine Matrix mit vier Einträgen kann vier Jobs planen. Wenn jedoch nur ein passender selbst gehosteter Runner online und frei ist, bleiben drei Jobs in der Warteschlange. Die sichtbare Matrixgröße ist dann vier, die tatsächliche Testkapazität aber weiterhin eins.
Die GitHub-Referenz zur Workflow-Syntax beschreibt unter anderem Matrix-Strategien, max-parallel und concurrency. Diese Optionen steuern den Workflow, stellen aber keine Hardware bereit. max-parallel begrenzt die gewünschte Anzahl gleichzeitig angeforderter Jobs. Die Zahl verfügbarer Runner, ihre Labels und ihre Runner-Gruppen bestimmen, wie viele Jobs tatsächlich starten können.
Vor der Aufteilung sollten Sie deshalb mindestens diese Werte getrennt erfassen:
- Wartezeit vom Einreihen bis zum Start auf einem Runner.
- Zeit für Checkout, Abhängigkeiten und Build.
- Reine Laufzeit des UI-Testsegments.
- Zeit für Artefakt-Upload und Ergebnisprüfung.
- Zahl der Erstfehler und Zahl der Fehler nach einer Wiederholung.
- Ressourcenbelegung des Mac während Simulatorstart und Testlauf.
Wenn Sie Build-Zeit, Warteschlange und Testausführung zu einer einzigen Dauer addieren, treffen Sie später leicht die falsche Kapazitätsentscheidung. Ein langsamer Workflow benötigt nicht automatisch mehr Remote Macs. Er kann ebenso durch unausgeglichene Segmente, gemeinsam genutzte Zustände oder wiederholte Builds gebremst werden.
Erste Phase: Die serielle Baseline dokumentieren
Führen Sie die vollständige UI-Suite zunächst mit einem festen Commit und einer festgehaltenen Umgebung aus. Dokumentieren Sie die Reihenfolge der Tests, die Laufzeit einzelner Gruppen, den ersten Fehler, das Simulatorziel und das erzeugte xcresult. Speichern Sie außerdem die Konsolenausgabe und Screenshots.
Die Apple-Dokumentation zum Ausführen und Interpretieren von Tests dient als Referenz für Testresultate und Ergebnispakete. Entscheidend ist, dass Sie nicht nur den finalen Workflow-Status betrachten. Ein grüner Job kann beispielsweise einen unvollständigen Artefakt-Upload verdecken, während ein roter Job durch einen nicht gestarteten Simulator und nicht durch eine fehlerhafte Assertion verursacht wurde.
Trennen Sie außerdem Build-Zeit und Test-Zeit. Wenn jedes Segment den vollständigen Build wiederholt, kann der zusätzliche Build-Aufwand einen Teil des Parallelisierungsvorteils aufzehren. Prüfen Sie zwei mögliche Ausführungswege:
- Jedes Segment führt seinen eigenen Build-und-Test-Lauf aus.
- Ein vorbereiteter Build wird mit
build-for-testingerzeugt und anschließend für getrennte Testläufe verwendet.
Die zweite Variante kann Wiederholungen reduzieren, erhöht aber die Anforderungen an Artefakttransport, Pfade und Reproduzierbarkeit. Wählen Sie sie erst, wenn der serielle Ablauf bereits zuverlässig dokumentiert ist.
Legen Sie auch einen Rückweg fest. Wenn die spätere Segmentierung fehlschlägt, muss die vollständige Suite mit demselben Scheme, derselben Codeversion und einem vergleichbaren Testkonto erneut ausführbar sein. Ohne diese Kontrollstrecke bleibt unklar, ob ein Test fehlt, ein Simulatorzustand beschädigt ist oder eine echte Regression vorliegt.
Zweite Phase: Testgruppen nach Abhängigkeiten schneiden
Die erste Aufteilung sollte nicht nach Dateianzahl erfolgen. Zehn kurze Tests können weniger Laufzeit verursachen als zwei lange Tests mit mehreren App-Zuständen. Teilen Sie stattdessen nach fachlich und technisch stabilen Grenzen:
- Test Target,
- Suite innerhalb eines Test Plans,
- unabhängiger Geschäftsbereich,
- kontrollierte
only-testing-Auswahl, - identische Daten- und Simulatoranforderung.
Die Apple-Anleitung zur Organisation von Tests beschreibt, wie Tests zur Verbesserung des Feedbacks organisiert werden können. Für Ihre CI-Pipeline bedeutet das: Ein Segment sollte einen klaren Zweck haben und allein reproduzierbar sein.
Folgende Tests sollten zunächst im selben Segment bleiben:
- Tests mit einem Login-Zustand, der nicht zuverlässig neu aufgebaut werden kann.
- Tests mit einem gemeinsam genutzten Konto.
- Tests mit einer gemeinsamen Push- oder Backend-Umgebung.
- Schritte eines Geschäftsprozesses, die zwingend aufeinander folgen.
- Tests, die denselben temporären Dateipfad oder dieselbe lokale Datenbank verändern.
- Tests, die eine externe Ressource in einer bestimmten Reihenfolge bearbeiten.
Vergeben Sie feste Kennungen wie ui-auth, ui-checkout oder ui-settings. Diese Namen sind nur Platzhalter. Jede Kennung benötigt eine definierte Testauswahl, ein Datenprofil, ein erwartetes Simulatorziel und einen eigenen Ergebnisordner.
Prüfen Sie die Aufteilung gegen die vollständige Baseline:
- Jeder relevante Test ist genau einem Segment zugeordnet.
- Kein Test wird versehentlich doppelt ausgeführt.
- Jedes Segment kann isoliert gestartet werden.
- Ein Fehler lässt sich mit derselben Segmentkennung wiederholen.
- Die Gesamtheit der Segmente deckt den Kontrolllauf vollständig ab.
Prüfen Sie verwendete xcodebuild-Optionen auf dem tatsächlichen Zielknoten mit xcodebuild -help. So vermeiden Sie, dass ein Parameter auf Ihrem Entwicklungsgerät funktioniert, auf dem Runner aber fehlt oder sich anders verhält.
Dritte Phase: Die Matrix auf passende Remote Macs routen
Erst nach der fachlichen Aufteilung wird aus jedem Segment ein eigener Matrix-Eintrag. Pro Eintrag sollten Sie mindestens folgende Informationen führen:
- Segmentkennung,
- Scheme oder Test Plan,
only-testing-Wert,- Simulator-Destination,
- Ergebnisverzeichnis,
- passendes Runner-Label,
- Daten- und Konto-Profil.
Verwenden Sie in Workflow-Beispielen nur Platzhalter wie <REPOSITORY>, <SCHEME>, <TEST_PLAN> und <SIMULATOR_DESTINATION>. Fest eingetragene Pfade oder Konten erschweren die Wiederverwendung auf weiteren Knoten und erhöhen das Risiko, dass parallele Jobs denselben Zustand verändern.
Drei Steuerungen müssen getrennt betrachtet werden:
Matrix: Sie bestimmt, welche Segmente als Jobs entstehen.
max-parallel: Sie begrenzt, wie viele Matrix-Jobs der Workflow gleichzeitig anfordern darf.
concurrency: Sie regelt, ob mehrere Workflow-Ausführungen gleichzeitig bestehen dürfen oder ob ein neuer Lauf einen älteren ersetzt.
Ein niedriger max-parallel-Wert kann sinnvoll sein, wenn die Runner-Gruppe nur begrenzte Ressourcen besitzt. Eine globale concurrency-Regel kann dagegen einen älteren Pull-Request-Lauf abbrechen, obwohl dieser gerade wichtige Fehlerdaten sammelt. Für schnelle Pull-Request-Rückmeldung kann ein neuer Lauf Vorrang erhalten. Für eine Release-Prüfung sollte der vollständige Lauf möglicherweise geschützt werden.
Die GitHub-Dokumentation zu Labels für selbst gehostete Runner erklärt die Auswahl geeigneter Runner. Labels sollten nicht nur „macOS“ bedeuten, sondern die wirklich erforderlichen Eigenschaften abbilden, etwa Xcode-Linie, Architektur, Simulatorbestand und Projektberechtigungen.
Prüfen Sie jeden Knoten vor dem ersten Matrix-Lauf:
- Ist der Runner online und der richtigen Gruppe zugewiesen?
- Ist der erwartete Xcode verfügbar?
- Startet die gewünschte Simulator-Destination?
- Sind Signing-, Keychain- und Projektberechtigungen vorhanden?
- Gibt es getrennte Arbeits- und Ergebnisverzeichnisse?
- Werden Artefakte eindeutig nach Segment und Job benannt?
Wenn Sie für einen zeitlich begrenzten Versuch eine verwaltete Mac-Umgebung benötigen, können Sie die Remote-Mac-Übersicht von KVMNODE als Ausgangspunkt für die Umgebungsprüfung nutzen. Die technische Entscheidung sollte jedoch erst nach Baseline und Zwei-Segment-Test fallen.
Vierte Phase: Simulatoren, Daten und Artefakte isolieren
Fehlende Isolation ist eine der häufigsten Ursachen für instabile erste Parallelversuche. Jeder Job benötigt ein kontrolliertes Simulatorziel und eigene Pfade für temporäre Daten. Besonders wichtig sind:
- Simulator-Destination oder Simulator-Instanz,
DerivedData,xcresult,- temporäres Arbeitsverzeichnis,
- Screenshots und Videos,
- lokale Datenbanken,
- Ports und Mock-Server,
- Testkonto und Testdaten.
Ein gemeinsamer DerivedData-Pfad kann Build-Artefakte überschreiben oder schwer reproduzierbare Zwischenzustände erzeugen. Ein identischer Ergebnisname kann dazu führen, dass der letzte Job das Resultat eines anderen Segments ersetzt. In beiden Fällen sieht der Fehler später wie ein zufälliger XCTest-Fehler aus.
Achtung: Ein Runner-Label beweist nur, dass ein Job ausgewählt werden kann. Es sagt nicht, dass ein zweiter Simulator auf diesem Knoten stabil läuft oder genügend Ressourcen für die gesamte Suite verfügbar sind.
Starten Sie mit einem Segment pro Runner. Testen Sie anschließend zwei Segmente auf zwei getrennten Remote Macs. Erst wenn dieser Versuch stabil ist, untersuchen Sie mehrere Simulatoren auf einem einzelnen Mac. So lassen sich Routingfehler, Knotenprobleme, Ressourcenengpässe und gemeinsam genutzte Testzustände voneinander unterscheiden.
Die interne Xcode-Parallelität ist nicht dasselbe wie mehrere GitHub-Jobs. Auch Swift-Testing-Prozesse innerhalb eines Jobs bilden eine eigene Ebene. Dokumentieren Sie deshalb bei jedem Versuch, ob Sie die Parallelität über Matrix, mehrere Runner, Xcode oder Prozesse innerhalb eines Jobs aktiviert haben.
Entscheidungswerkzeug: Welche Parallelitätsstufe ist jetzt vertretbar?
Verwenden Sie die folgende Bedingungsliste nach jedem Versuch. Markieren Sie nur Punkte, die mit Logs, Runner-Daten oder Testresultaten belegt sind:
- [ ] Wenn die Matrix-Jobs auf passende freie Runner verteilt werden und die Wartezeit den Hauptanteil der Pipeline bildet, wählen Sie zusätzliche getrennte Remote-Mac-Runner. Sonst bleiben Sie bei der aktuellen Knotenzahl.
- [ ] Wenn jedes Segment isolierte Simulatoren,
DerivedData-Pfade, Ergebnisordner und Testdaten besitzt, dürfen Sie einen kontrollierten Parallelversuch starten. Sonst stoppen Sie die Ausweitung und beheben zuerst die gemeinsamen Zustände. - [ ] Wenn ein einzelner Mac mehrere Segmente ohne erhöhte Infrastrukturfehler ausführt und seine Ressourcenbelegung nachvollziehbar bleibt, können Sie lokale Simulator-Parallelität begrenzt testen. Sonst führen Sie die Segmente auf getrennten Knoten aus.
- [ ] Wenn ein Segment regelmäßig deutlich länger läuft als die anderen, überarbeiten Sie seine fachlichen Grenzen. Sonst behalten Sie die aktuelle Verteilung zunächst bei.
- [ ] Wenn Erstlauf und Wiederholung unterschiedliche Ergebnisse liefern, klassifizieren Sie den Test als instabil und erhöhen nicht die Parallelität. Sonst darf der nächste Kapazitätstest beginnen.
- [ ] Wenn alle erwarteten
xcresult-Dateien vorliegen und jeder Fehler einer Segmentkennung zugeordnet werden kann, gehen Sie zur Ergebnisbewertung über. Sonst behandeln Sie den Lauf als unvollständig. - [ ] Wenn die parallelen Resultate noch nicht mit der seriellen Kontrollstrecke übereinstimmen, behalten Sie die serielle oder duale Release-Prüfung bei. Sonst können Sie die Matrix schrittweise in den Standardworkflow übernehmen.
Diese Liste verhindert, dass Sie allein aufgrund einer größeren Matrix oder einer kürzeren Einzelmessung eine dauerhafte Architekturentscheidung treffen. Ein erfüllter Punkt benötigt eine überprüfbare Beobachtung. Vermutungen über verfügbare Runner, Simulatorgrenzen oder erwartete Beschleunigung reichen nicht aus.
Fünfte Phase: xcresult-Dateien vollständig sammeln
Jeder Matrix-Job sollte unabhängig folgende Artefakte hochladen:
- vollständiges
xcresult, - Konsolenausgabe von
xcodebuild, - Screenshots und Videos,
- Segmentkennung,
- Commit, Scheme, Test Plan und Simulator-Destination,
- Kennzeichnung für Erstlauf oder Wiederholung.
Laden Sie Artefakte auch bei einem fehlgeschlagenen Test hoch. Speichern Sie sie in einem eindeutigen Pfad, der mindestens Segment und Job identifiziert. Der Gesamtworkflow darf nicht nur „grün“ oder „rot“ auswerten, sondern muss zwischen einem fehlenden Ergebnis und einem echten Assertion-Fehler unterscheiden.
Beginnen Sie die Zusammenführung mit einer Vollständigkeitsprüfung:
- Stimmen erwartete und eingegangene Segmentkennungen überein?
- Wurde jedes Segment gestartet?
- Fehlt ein
xcresultwegen eines Upload- oder Pfadfehlers? - Ist der Simulator gestartet und die App installiert worden?
- Enthält das Ergebnis einen echten Testfehler?
Danach klassifizieren Sie die Ursache:
- Infrastrukturfehler: Runner nicht verfügbar, Simulatorstart fehlgeschlagen oder Berechtigung fehlt.
- Ausführungsfehler: Installation, Timeout oder Prozessabbruch.
- Assertion-Fehler: Das Produktverhalten entspricht nicht der Erwartung.
- Instabiler Test: Derselbe Commit liefert ohne Codeänderung unterschiedliche Ergebnisse.
Ein Wiederholungslauf ist ein Diagnosewerkzeug. Er darf den ersten Fehler nicht überschreiben. Wenn ein Test zunächst fehlschlägt und bei der Wiederholung erfolgreich ist, muss der Workflow die vorhandene Instabilität weiterhin sichtbar machen.
Bei Coverage dürfen Sie Prozentwerte aus mehreren Segmenten nicht addieren. Eine korrekte Zusammenführung benötigt dieselbe Quellgrundlage und ein dafür geeignetes offizielles Auswertungsverfahren. Die Apple-Dokumentation zu Testresultaten sollte dabei die Referenz für Ihre Auswertung bleiben.
FAQ: Die vier häufigsten Umsetzungsentscheidungen
Wie werden mehrere iOS-UI-Testgruppen in GitHub Actions parallel ausgeführt?
Zuerst definieren Sie unabhängige Gruppen über Xcode Test Plans, Test Targets oder eine kontrollierte only-testing-Auswahl. Danach erzeugt eine Matrix für jede Gruppe einen eigenen Job. Tatsächliche Parallelität entsteht aber erst, wenn passende freie Runner vorhanden sind. Zeigen alle Jobs auf einen einzigen selbst gehosteten Mac, werden sie weiterhin nacheinander eingeplant.
Wie viele Simulator-Tests kann ein einzelner Mac Runner gleichzeitig ausführen?
Dafür gibt es keine allgemein gültige Zahl. Xcode-Version, Projektgröße, Arbeitsspeicher, CPU-Last, Simulatorzustand und Testdaten beeinflussen die Grenze. Beginnen Sie mit einem Simulator pro Job, messen Sie Ressourcen und vergleichen Sie Erstlauf, Wiederholung und Infrastrukturfehler. Erhöhen Sie die lokale Parallelität nur, wenn alle Arbeits-, Ergebnis- und Datenpfade getrennt sind.
Wie gruppieren Sie Xcode UI-Tests über einen Test Plan?
Ordnen Sie Tests nach stabilen technischen und fachlichen Abhängigkeiten, nicht nach einer gleichen Zahl von Dateien. Tests mit gemeinsamem Login, Konto, Push-Dienst oder einer festen Geschäftsprozessfolge bleiben zusammen. Unabhängige Bereiche lassen sich über Test Plans oder only-testing trennen. Nach jeder Änderung müssen Sie Abdeckung und Duplikate gegen den seriellen Kontrolllauf prüfen.
Wie lassen sich mehrere xcresult-Dateien aus Matrix-Jobs auswerten?
Jeder Job lädt sein xcresult, Logs, Screenshots und eine Segmentbeschreibung unter einer eindeutigen Kennung hoch. Die Auswertung prüft zuerst fehlende Segmente und trennt danach Infrastrukturfehler von Assertion-Fehlern. Ein fehlendes Ergebnispaket darf nicht als erfolgreicher Test gelten. Coverage-Prozentwerte werden nicht addiert, sondern mit einem geeigneten Auswertungsverfahren auf derselben Grundlage geprüft.
Sechste Phase: Nach echten Pull Requests die Betriebsform festlegen
Nach der ersten Implementierung sollten Sie nicht sofort eine dauerhafte Runner-Flotte festlegen. Beobachten Sie mehrere echte Pull Requests und mindestens einen vollständigen Kontrolllauf. Relevant sind:
- Ende-zu-Ende-Feedbackzeit,
- Runner-Wartezeit,
- Laufzeit des längsten Segments,
- Unterschiede zwischen den Segmenten,
- Infrastrukturfehler,
- Wiederholungs- und Flake-Rate,
- Ressourcenbelegung je Mac,
- Vollständigkeit der Artefakte.
Wenn das längste Segment regelmäßig die Gesamtzeit bestimmt, liegt der nächste Hebel wahrscheinlich in einer besseren Testgrenze. Teilen Sie es nur weiter, wenn seine internen Abhängigkeiten sauber getrennt werden können. Eine künstliche Aufteilung kann zusätzliche Datenvorbereitung, mehr Simulatorstarts und mehr Fehlerquellen erzeugen.
Wenn die vorbereiteten Tests dagegen lange auf passende Runner warten, ist zusätzliche Remote-Mac-Kapazität der sinnvollere Ansatz. Mehr Matrix-Einträge ohne weitere Runner verkürzen die Wartezeit nicht. Für eine langfristige Hardwareentscheidung können Sie die KVMNODE-Informationen zu Mac-mini-Umgebungen als Vergleichspunkt heranziehen. Ausschlaggebend bleiben Ihre tatsächliche Auslastung, Zugriffsvorgaben und geplante Betriebsdauer.
Ein eigener Mac mini kann bei dauerhaft hoher und gleichmäßiger Last sinnvoll sein. Dafür entstehen jedoch Anschaffung, Wartung, Updates, Stromversorgung und die Verantwortung für die physische Verfügbarkeit. Eine virtuelle macOS- oder Linux-Umgebung kann einzelne Build-Schritte abdecken, ersetzt aber nicht zwingend einen stabilen Simulator-Testpfad mit macOS-Rechten.
Ein gemieteter Remote Mac eignet sich eher für einen zeitlich begrenzten Kapazitätstest oder wechselnde Lasten. Sie vermeiden zunächst den Kauf eines weiteren Geräts und können getrennte Runner für die Matrix einrichten. Nachteile bleiben: Netzwerkzugriff, mögliche Latenz bei interaktiver Fehlersuche, Abhängigkeit von der Verfügbarkeit sowie zusätzliche Abstimmung bei Keychain- und Signing-Prozessen. Bei dauerhaft sehr hoher Last oder zwingenden physischen Schnittstellen kann eigene Hardware die bessere Wahl sein.
Wenn Sie nach der Baseline und einem Zwei-Segment-Lauf nachweisen, dass fehlende Runner statt gemeinsamer Testzustände bremsen, beginnen Sie mit einem klar begrenzten Remote-Mac-Versuch bei KVMNODE. Definieren Sie vorher die Segmentliste, die Simulatorziele, die Artefaktpfade und den seriellen Rückweg. So bewerten Sie nicht nur die Anzahl sichtbarer Matrix-Jobs, sondern die reale Wartezeit, Stabilität und Rückverfolgbarkeit Ihrer iOS-CI.