Einleitung
\nIntegrationstests sind eine kritisch wichtige Entwicklungsphase, die das Zusammenspiel der Komponenten Ihrer Swift-Anwendung mit externen Abhängigkeiten überprüft: Datenbanken, APIs, Dateisysteme und Netzwerkdienste. Traditionelle Ansätze für Integrationstests unter macOS/iOS leiden jedoch oft unter dem "Umgebungsproblem" – Tests, die auf dem Entwicklerrechner funktionieren, können auf dem CI-Server aufgrund von Unterschieden in Bibliotheksversionen, Netzwerkeinstellungen oder Datenbankkonfigurationen fehlschlagen.
\nDocker löst dieses Problem grundlegend: Sie packen alle Abhängigkeiten (PostgreSQL, Redis, Mock-Server) in isolierte Container, die auf jeder Plattform identisch ausgeführt werden. In diesem Artikel betrachten wir fortgeschrittene Techniken für Integrationstests von Swift mit Docker: von der Einrichtung der Testumgebung bis zur parallelen Ausführung und Integration in CI/CD.
\n\n1. Vorbereitung der Testumgebung mit Docker Compose
\nBeginnen wir mit der Erstellung einer isolierten Umgebung für die Tests. Anstatt jeden Dienst manuell zu starten, verwenden wir docker-compose.yml zur deklarativen Beschreibung aller Abhängigkeiten.
Beispiel docker-compose.yml für ein Swift-Projekt
\nversion: '3.8'\nservices:\n postgres_test:\n image: postgres:15-alpine\n environment:\n POSTGRES_USER: test_user\n POSTGRES_PASSWORD: test_pass\n POSTGRES_DB: swift_test_db\n ports:\n - "5433:5432" # Verwenden einen nicht standardmäßigen Port, um Konflikte mit lokaler DB zu vermeiden\n tmpfs: /var/lib/postgresql/data # Daten im Arbeitsspeicher – schnelle Bereinigung\n\n redis_test:\n image: redis:7-alpine\n ports:\n - "6380:6379"\n\n mock_server:\n build: ./mock-server # Benutzerdefinierter Mock-Server auf Vapor\n ports:\n - "8081:8080"\n environment:\n LOG_LEVEL: debug\n\nSwift-Code für dynamische Testkonfiguration
\nDamit die Tests eine Verbindung zu den Docker-Containern und nicht zu lokalen Diensten herstellen, verwenden wir Umgebungsvariablen:
\nimport XCTest\n\nfinal class IntegrationTests: XCTestCase {\n var app: XCTestApplication?\n\n override func setUp() async throws {\n // Konfiguration aus Umgebungsvariablen abrufen (gesetzt in CI oder docker-compose)\n let dbHost = ProcessInfo.processInfo.environment["DB_HOST"] ?? "localhost"\n let dbPort = Int(ProcessInfo.processInfo.environment["DB_PORT"] ?? "5433") ?? 5433\n let redisHost = ProcessInfo.processInfo.environment["REDIS_HOST"] ?? "localhost"\n let mockHost = ProcessInfo.processInfo.environment["MOCK_HOST"] ?? "http://localhost:8081"\n\n // Testanwendung konfigurieren (z. B. Vapor oder Kitura)\n let config = AppConfig(\n database: DatabaseConfig(host: dbHost, port: dbPort, \n username: "test_user", password: "test_pass",\n database: "swift_test_db"),\n redis: RedisConfig(host: redisHost, port: 6380),\n mockServiceURL: mockHost\n )\n\n app = try await XCTestApplication.start(config: config)\n }\n\n override func tearDown() async throws {\n try await app?.shutdown()\n }\n}\n\n2. Fortgeschrittene Einrichtung: Parallele Ausführung und Testisolierung
\nEines der Hauptprobleme von Integrationstests ist ihre Ausführungszeit. Docker ermöglicht das parallele Starten mehrerer Kopien der Testumgebung, was den Durchlauf erheblich beschleunigt.
\n\nIsolierungsstrategie: Ein Container pro Testklasse
\nWir verwenden docker-compose --project-name, um eindeutige Instanzen zu erstellen:
#!/bin/bash\n# Starten von Tests mit eindeutigem project-name für parallele Ausführung\nTEST_SUITE=$1\nPROJECT_NAME="swift_test_${TEST_SUITE}_$(date +%s)"\n\ndocker-compose -p $PROJECT_NAME up -d --force-recreate\nsleep 5 # Warten auf Initialisierung der Container\n\n# Bestimmte Testklasse ausführen\nxcodebuild test -scheme MyApp -destina