TDD en Swift : comment écrire du code qui ne casse pas

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

« D'abord le test — ensuite le code » : pourquoi ça fonctionne en Swift ?

\n\n

Avez-vous déjà réécrit le même module pour la troisième fois parce qu'il fallait « le lancer en urgence » ? Ou attrapé un bug en production qu'il aurait été plus simple d'éviter lors de l'écriture ? Si oui — bienvenue dans le monde du TDD (Test-Driven Development).

\n\n

Le développement piloté par les tests n'est pas qu'une simple méthodologie à la mode. C'est une façon de penser qui vous oblige à formuler comment le code doit se comporter avant même de l'écrire. En Swift, avec son typage strict et son puissant framework XCTest, le TDD devient un bouclier fiable contre les régressions et les crashs inattendus.

\n\n

Dans cet article, nous allons décortiquer le TDD avec des exemples concrets, écrire des tests pour la couche réseau et le modèle de données, et surtout — comprendre comment adopter cette pratique sans douleur. C'est parti !

\n\n

Rouge-Vert-Refactorisation : la magie du cycle

\n\n

La base du TDD est un cycle en trois étapes :

\n\n
    \n
  • Rouge (Red) — on écrit un test qui échoue (parce que le code n'existe pas encore).
  • \n
  • Vert (Green) — on écrit le code minimal pour que le test passe.
  • \n
  • Refactorisation (Refactor) — on améliore le code sans changer son comportement (les tests protègent).
  • \n
\n\n

Cela semble simple ? Essayons en Swift.

\n\n

Premier test : vérifier le modèle User

\n\n

Supposons que nous ayons un modèle utilisateur simple. Nous voulons qu'il formate correctement le nom complet. D'abord — le test :

\n\n
import XCTest\n@testable import MonApp\n\nfinal class UserTests: XCTestCase {\n    \n    func testFullName_GivenFirstNameAndLastName_ReturnsFormattedString() {\n        // Arrange\n        let user = User(firstName: "Anna", lastName: "Karénine")\n        \n        // Act\n        let fullName = user.fullName()\n        \n        // Assert\n        XCTAssertEqual(fullName, "Anna Karénine", "Le nom complet doit être composé du prénom et du nom séparés par un espace")\n    }\n}\n
\n\n

On lance — le test échoue (rouge). Le compilateur râle car User n'existe pas. Parfait, c'est prévu.

\n\n

Maintenant, on écrit le code minimal pour faire passer le test :

\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

On lance — vert ! On peut passer à la refactorisation. Par exemple, ajouter la gestion des chaînes vides ou du deuxième prénom. Mais ne vous précipitez pas — les tests vous indiqueront quand c'est nécessaire.

\n\n

Tester la couche réseau asynchrone

\n\n

L'un des problèmes les plus fréquents en développement iOS — le travail avec le réseau. Le TDD est particulièrement utile ici car vous séparez clairement la logique des appels réseau.

\n\n

Écrire le protocole et l'objet mock

\n\n

D'abord, définissons à quoi notre service doit ressembler :

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

Maintenant — le test. Nous ne voulons pas dépendre d'une API réelle, donc nous créons un 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: "ensoleillé")\n        let viewModel = WeatherViewModel(service: mockService)\n        let expectation = XCTestExpectation(description: "Attente du chargement de la météo")\n        \n                

Blogs

Book Recommendations