Testen in Swift: Unit, Integration, E2E – Fortgeschrittenes Niveau

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Fortgeschrittenes Testen in Swift: Unit, Integration und E2E



Testen ist nicht nur eine Überprüfung, sondern das Fundament zuverlässigen Codes. Auf fortgeschrittenem Niveau muss ein Swift-Entwickler nicht nur Tests schreiben, sondern die Testpyramide geschickt aufbauen: von schnellen Unit-Tests bis hin zu komplexen End-to-End (E2E)-Szenarien. In diesem Artikel analysieren wir fortgeschrittene Techniken für iOS/macOS-Projekte: die Arbeit mit Abhängigkeiten, Integrationstests mit CoreData und UI-Automatisierung.



1. Unit-Tests: Abhängigkeitsverwaltung und Mocks



Auf fortgeschrittenem Niveau müssen Unit-Tests isoliert sein. Die Schlüsseltechnik ist Dependency Injection (DI). Anstatt die echte Netzwerkschicht oder Datenbank zu testen, ersetzen wir sie durch Mocks.



Beispiel: Protokoll und DI

protocol NetworkServiceProtocol {    func fetchData(completion: @escaping (Result<String, Error>) -> Void)}

class ViewModel { private let networkService: NetworkServiceProtocol init(networkService: NetworkServiceProtocol) { self.networkService = networkService } func loadData() { networkService.fetchData { result in // Verarbeitung } }}

// Mock für Testsclass MockNetworkService: NetworkServiceProtocol { var shouldReturnError = false func fetchData(completion: @escaping (Result<String, Error>) -> Void) { if shouldReturnError { completion(.failure(NSError(domain: "test", code: -1))) } else { completion(.success("Mock data")) } }}


Im Test erstellen Sie MockNetworkService und übergeben es an ViewModel. Dies ermöglicht die Überprüfung von Fehler- und Erfolgsszenarien ohne echte API.



XCTestExpectation für Asynchronität

func testViewModelLoadDataSuccess() {    let mock = MockNetworkService()    let viewModel = ViewModel(networkService: mock)        let expectation = self.expectation(description: "Data loaded")        viewModel.loadData()        // Simuliere asynchronen Aufruf    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {        expectation.fulfill()    }        waitForExpectations(timeout: 1.0)    // Überprüfe den Zustand des ViewModel}


2. Integrationstests: CoreData und Arbeit mit echten Daten



Integrationstests überprüfen das Zusammenspiel von Komponenten. Für CoreData verwenden wir einen In-Memory-Container, um die Hauptdatenbank nicht zu verschmutzen.



Einrichtung von In-Memory CoreData

class CoreDataTestStack {    lazy var persistentContainer: NSPersistentContainer = {        let container = NSPersistentContainer(name: "MyModel")        let description = NSPersistentStoreDescription()        description.type = NSInMemoryStoreType        container.persistentStoreDescriptions = [description]        container.loadPersistentStores { _, error in            if let error = error {                fatalError("Failed to load store: \\(error)")            }        }        return container    }()        var context: NSManagedObjectContext {        return persistentContainer.viewContext    }}


Beispiel eines Integrationstests

func testSaveAndFetchUser() {    let stack = CoreDataTestStack()    let repository = UserRepository(context: stack.context)        // Erstelle einen Benutzer    let user = User(context: stack.context)    user.name = "Test User"    user.age = 30        try? stack.context.save()        // Überprüfe, ob die Daten gespeichert wurden    let fetchRequest: NSFetchRequest<User> = User.fetchRequest()    let users = try? stack.context.fetch(fetchRequest)        XCTAssertEqual(users?.count, 1)    XCTAssertEqual(users?.first?.name, "Test User")}


Wichtig: Bereinigen Sie nach jedem Test den Kontext, damit die Tests sich nicht gegenseitig beeinflussen.

Blogs

Buchempfehlungen