Einleitung: Warum Testen die Grundlage einer zuverlässigen Swift-App ist
\n\nDie Entwicklung für iOS und macOS erfordert hohe Code-Zuverlässigkeit. Ein Fehler in der Anwendungslogik kann das Benutzererlebnis kosten und in kommerziellen Projekten Geld. Testen in Swift ist nicht nur „guter Stil“, sondern eine obligatorische Entwicklungsphase. Es ermöglicht, Bugs frühzeitig zu erkennen, Refactoring zu vereinfachen und die Codequalität zu verbessern.
\n\nIn diesem Artikel betrachten wir drei Testebenen: Unit (Modultests), Integration (Integrationstests) und E2E (End-to-End-Tests). Sie erfahren, wie sie sich unterscheiden, wann Sie welche anwenden, und sehen funktionierende Beispiele in Swift mit dem Framework XCTest.
1. Unit-Testen in Swift: Die Grundlage
\n\nUnit-Tests überprüfen einzelne Module (Funktionen, Methoden, Klassen) isoliert. Ihr Hauptziel ist es, sicherzustellen, dass jede „Einheit“ des Codes korrekt funktioniert.
\n\nEinrichtung von XCTest
\n\nXCTest ist in Xcode integriert. So erstellen Sie ein Test-Target:
\n- \n
- Öffnen Sie das Projekt in Xcode. \n
- Wählen Sie File → New → Target. \n
- Wählen Sie Unit Testing Bundle. \n
Nach der Erstellung erhalten Sie eine Datei namens YourAppTests.swift.
Beispiel eines Unit-Tests
\n\nAngenommen, wir haben eine Funktion zur Berechnung eines Rabatts:
\n\nfunc calculateDiscount(price: Double, discount: Double) -> Double {\n guard discount >= 0 && discount <= 100 else { return price }\n return price * (1 - discount / 100)\n}\n\n\nWir schreiben einen Test:
\n\nimport XCTest\n@testable import YourApp\n\nclass DiscountTests: XCTestCase {\n func testCalculateDiscount_ValidInput() {\n let result = calculateDiscount(price: 1000, discount: 20)\n XCTAssertEqual(result, 800, accuracy: 0.01)\n }\n \n func testCalculateDiscount_InvalidDiscount() {\n let result = calculateDiscount(price: 500, discount: 150)\n XCTAssertEqual(result, 500)\n }\n}\n\n\nBest Practices für Unit-Tests:
\n- \n
- Ein Test – eine Überprüfung. \n
- Verwenden Sie
setUp()zur Initialisierung gemeinsamer Objekte. \n - Vermeiden Sie Abhängigkeiten von Netzwerk, DB oder UI – verwenden Sie Mocks. \n
2. Integrationstests: Überprüfung der Komponenteninteraktion
\n\nIntegrationstests überprüfen, wie verschiedene Module zusammenarbeiten. Zum Beispiel die Interaktion eines Datenmodells mit der Netzwerkschicht oder der CoreData-Datenbank.
\n\nBeispiel: Testen einer Netzwerkanfrage
\n\nWir erstellen einen einfachen Service:
\n\nclass NetworkService {\n func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {\n // Echte API-Anfrage\n }\n}\n\n\nEin Integrationstest kann überprüfen, ob der Service die Antwort eines echten Servers (oder eines lokalen Mock-Servers) korrekt verarbeitet:
\n\nclass NetworkServiceIntegrationTests: XCTestCase {\n var service: NetworkService!\n \n override func setUp() {\n super.setUp()\n service = NetworkService()\n }\n \n func testFetchUser_ReturnsUser() {\n let expectation = self.expectation(description: "Fetch user")\n \n service.fetchUser { result in\n switch result {\n case .success(let user):\n XCTAssertNotNil(user.name)\n case .failure:\n XCTFail("Request failed")\n }\n expectation.fulfill()\n }\n \n waitForExpectations(timeout: 5)\n }\n}\n\n\nWichtig: Integrationstests sind langsamer als Unit-Tests, daher werden sie seltener ausgeführt (z. B. vor dem Merge in den main-Branch).
\n\n3. E2E-Testen (End-to-End) mit XCUITest
\n\nE2E-Tests (End-to-End) simulieren Benutzeraktionen: Tastendrücke, Texteingabe, Überprüfung der UI-Anzeige. In Swift wird dafür