TDD in Swift: Wie man Code schreibt, der nicht kaputt geht

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

„Erst der Test – dann der Code“: Warum funktioniert das in Swift?

\n\n

Haben Sie schon einmal dasselbe Modul zum dritten Mal neu geschrieben, weil „es schnell ausgeliefert werden musste“? Oder einen Bug in der Produktion gefunden, der sich beim Schreiben leicht hätte vermeiden lassen? Wenn ja – willkommen in der Welt von TDD (Test-Driven Development).

\n\n

Testgetriebene Entwicklung ist nicht nur eine modische Methodik. Es ist eine Denkweise, die Sie dazu zwingt zu formulieren, wie sich der Code verhalten soll, bevor Sie ihn schreiben. In Swift, mit seiner strengen Typisierung und dem leistungsstarken Framework XCTest, wird TDD zu einem zuverlässigen Schutzschild gegen Regressionen und unerwartete Abstürze.

\n\n

In diesem Artikel werden wir TDD anhand realer Beispiele durchgehen, Tests für die Netzwerkschicht und das Datenmodell schreiben und vor allem verstehen, wie man diese Praxis schmerzfrei einführt. Los geht's!

\n\n

Rot-Grün-Refactoring: Die Magie des Zyklus

\n\n

Die Grundlage von TDD ist ein Dreischritt-Zyklus:

\n\n
    \n
  • Rot (Red) – Wir schreiben einen Test, der fehlschlägt (weil der Code noch nicht existiert).
  • \n
  • Grün (Green) – Wir schreiben den minimalen Code, damit der Test besteht.
  • \n
  • Refactoring (Refactor) – Wir verbessern den Code, ohne sein Verhalten zu ändern (die Tests schützen uns).
  • \n
\n\n

Klingt einfach? Probieren wir es mit Swift.

\n\n

Erster Test: Überprüfung des User-Modells

\n\n

Angenommen, wir haben ein einfaches Benutzermodell. Wir möchten, dass es den vollständigen Namen korrekt formatiert. Zuerst – der Test:

\n\n
import XCTest\n@testable import MyApp\n\nfinal class UserTests: XCTestCase {\n    \n    func testFullName_GivenFirstNameAndLastName_ReturnsFormattedString() {\n        // Arrange\n        let user = User(firstName: "Anna", lastName: "Karenina")\n        \n        // Act\n        let fullName = user.fullName()\n        \n        // Assert\n        XCTAssertEqual(fullName, "Anna Karenina", "Der vollständige Name sollte aus Vor- und Nachnamen mit Leerzeichen bestehen")\n    }\n}\n
\n\n

Wir starten – der Test schlägt fehl (rot). Der Compiler beschwert sich, dass User nicht existiert. Perfekt, so ist es beabsichtigt.

\n\n

Jetzt schreiben wir den minimalen Code, um den Test zu bestehen:

\n\n
struct User {\n    let firstName: String\n    let lastName: String\n    \n    func fullName() -> String {\n        return "\\(firstName) \\(lastName)"\n    }\n}\n
\n\n

Wir starten – grün! Wir können zum Refactoring übergehen. Zum Beispiel die Behandlung von leeren Zeichenketten oder einem middle name hinzufügen. Aber beeilen Sie sich nicht – die Tests werden zeigen, wann dies nötig ist.

\n\n

Testen der asynchronen Netzwerkschicht

\n\n

Eines der häufigsten Probleme in der iOS-Entwicklung ist die Netzwerkarbeit. TDD ist hier besonders nützlich, weil Sie die Logik explizit von den Netzwerkaufrufen trennen.

\n\n

Schreiben eines Protokolls und eines Mock-Objekts

\n\n

Zuerst definieren wir, wie unser Dienst aussehen soll:

\n\n
protocol WeatherServiceProtocol {\n    func fetchWeather(for city: String, completion: @escaping (Result<Weather, Error>) -> Void)\n}\n
\n\n

Jetzt – der Test. Wir wollen nicht von einer echten API abhängen, daher erstellen wir einen Mock:

\n\n
class MockWeatherService: WeatherServiceProtocol {\n    var shouldReturnError = false\n    var mockWeather: Weather?\n    \n    func fetchWeather(for city: String, completion: @escaping (Result<Weather, Error>) -> Void) {\n        if shouldReturnError {\n            completion(.failure(NetworkError.serverError))\n        } else if let weather = mockWeather {\n            completion(.success(weather))\n        }\n    }\n}\n\nfinal class WeatherViewModelTests: XCTestCase {\n    \n    func testFetchWeather_Success_UpdatesModel() {\n        // Arrange\n        let mockService = MockWeatherService()\n        mockService.mockWeather = Weather(temperature: 25.0, condition: "sunny")\n        let viewModel = WeatherViewModel(service: mockService)\n        let expectation = XCTestExpectation(description: "Warten auf das Laden des Wetters")\n        \n                

Blogs

Buchempfehlungen