Integrationstests von Swift mit Docker: Fortgeschrittenes Niveau
Integrationstests sind eine Schlüsselphase bei der Entwicklung zuverlässiger Swift-Anwendungen. Im Gegensatz zu Unit-Tests überprüfen sie die Interaktion zwischen realen Komponenten: Datenbank, Netzwerkdienste, Dateisystem. Docker ermöglicht es, diese Abhängigkeiten zu isolieren, wodurch Tests reproduzierbar und unabhängig von der Entwicklungsumgebung werden. In diesem Artikel betrachten wir fortgeschrittene Techniken für Integrationstests von Swift-Anwendungen (iOS und serverseitig) unter Verwendung von Docker.
1. Grundlagen der Docker-Containerisierung für Swift-Tests
Bevor wir uns in fortgeschrittene Szenarien vertiefen, ist es wichtig, die grundlegende Architektur zu verstehen. Docker ermöglicht es, Abhängigkeiten (PostgreSQL, Redis, Mock-Server) in isolierten Containern auszuführen, die automatisch vor den Tests gestartet und danach zerstört werden.
Für serverseitiges Swift (Vapor, Kitura) verwenden wir das offizielle Swift-Image. Für iOS-Anwendungen kann Docker keinen Simulator starten, eignet sich aber hervorragend zum Testen der Netzwerkschicht und des Backends.
Beispiel einer grundlegenden Docker Compose-Datei für ein Vapor-Projekt:
version: '3.8'services: app: image: swift:5.10-focal working_dir: /app volumes: - .:/app depends_on: - db - redis environment: - DATABASE_URL=postgres://user:pass@db:5432/testdb - REDIS_URL=redis://redis:6379
db: image: postgres:16-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: pass POSTGRES_DB: testdb ports: - "5432:5432"
redis: image: redis:7-alpine ports: - "6379:6379"Diese Datei startet drei Container: den Swift-Server selbst, PostgreSQL und Redis. Nun schreiben wir einen Integrationstest, der das Speichern eines Benutzers in einer echten Datenbank überprüft.
2. Schreiben von Integrationstests mit XCTest und Docker
Für Integrationstests von serverseitigem Swift verwenden wir das Standard-Framework XCTest. Der entscheidende Punkt ist, dass die Tests innerhalb des Containers ausgeführt werden müssen, der Zugriff auf die anderen Container hat.
Beispiel eines Tests für eine Vapor-Anwendung:
import XCTest@testable import Appimport Vapor
final class UserIntegrationTests: XCTestCase {
var app: Application!
override func setUp() async throws { app = try await Application.make(.testing) try await configure(app)
// Datenbankmigration vor jedem Test try await app.autoMigrate() }
override func tearDown() async throws { try await app.autoRevert() try await app.asyncShutdown() }
func testCreateAndFetchUser() async throws { // Benutzer über API erstellen let userDTO = UserDTO(name: "Alice", email: "alice@test.com") try await app.test(.POST, "/users", beforeRequest: { req in try req.content.encode(userDTO) }, afterResponse: { res in XCTAssertEqual(res.status, .created) let createdUser = try res.content.decode(User.self) XCTAssertEqual(createdUser.name, "Alice") })
// Überprüfen, ob der Benutzer in der Datenbank gespeichert wurde try await app.test(.GET, "/users/\\(aliceID)", afterResponse: { res in XCTAssertEqual(res.status, .ok) let fetchedUser = try res.content.decode(User.self) XCTAssertEqual(fetchedUser.email, "alice@test.com") }) }}Dieser Test startet die Anwendung, führt HTTP-Anfragen an den realen Server innerhalb des Containers aus und überprüft die Antworten. Docker stellt sicher, dass die Datenbank und Redis für jeden Durchlauf sauber sind.
3. Mock-Server und Isolierung externer Abhängigkeiten
Auf fortgeschrittenem Niveau ist es oft erforderlich, die Interaktion mit externen APIs (Zahlungssysteme, Wetterdienste) zu testen.