Tests de mutation en Swift : l'arme secrète pour un code parfait

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

Vos tests mentent, et vous ne le savez même pas

\n\n

Imaginez la situation : vous avez écrit 200 tests unitaires, la couverture de code est à 95%, l'intégration continue est verte, le manager est content. Vous publiez la version, et une semaine plus tard, la production plante à cause d'un bug que les tests auraient dû détecter. Cela vous dit quelque chose ? Bienvenue dans le monde de la fausse confiance, où le mutation testing (tests de mutation) agit comme un miroir honnête pour votre code Swift.

\n\n

Dans cet article, nous allons voir ce que sont les tests de mutation en Swift, comment ils fonctionnent en pratique et pourquoi chaque développeur iOS devrait les ajouter à son arsenal. Pas de théorie ennuyeuse – seulement du code, des exemples et des cas concrets.

\n\n

Qu'est-ce que le test de mutation et pourquoi en a-t-on besoin ?

\n\n

Le test de mutation (mutation testing) est une méthode d'évaluation de la qualité des tests en introduisant de petites modifications (mutations) dans le code source. Ces versions modifiées sont appelées mutants. Si les tests ne détectent pas le mutant (c'est-à-dire qu'ils ne tombent pas en échec), cela signifie que votre couverture de test est faible et que ce mutant survit.

\n\n

Pourquoi est-ce important ? Les métriques traditionnelles comme la couverture de code (code coverage) montrent uniquement quel pourcentage de code a été exécuté pendant les tests. Mais elles ne répondent pas à la question principale : les tests vérifient-ils la justesse de la logique ? Le test de mutation apporte la réponse : si vous modifiez accidentellement la condition if a > b en if a < b, vos tests le remarqueront-ils ?

\n\n

Comment fonctionne le test de mutation en Swift : explication simple

\n\n

Le processus se déroule ainsi :

\n\n
    \n
  1. Code source — votre fichier Swift de travail.
  2. \n
  3. Génération de mutants — le programme crée automatiquement de nombreuses versions du code avec une petite erreur (par exemple, il remplace + par -).
  4. \n
  5. Exécution des tests — pour chaque mutant, l'ensemble complet des tests unitaires est exécuté.
  6. \n
  7. Analyse des résultats — si les tests échouent, le mutant est « tué ». Si les tests réussissent, le mutant a « survécu », et c'est un signal d'alarme.
  8. \n
\n\n

Le pourcentage de mutants tués s'appelle le mutation score. Plus il est élevé, plus vos tests sont fiables.

\n\n

Exemple pratique : écrire du code et des tests en Swift

\n\n

Imaginons que nous écrivions un simple calculateur de réductions pour une boutique en ligne en Swift. Voici notre code de production :

\n\n
// DiscountCalculator.swift\nstruct DiscountCalculator {\n    func calculateDiscount(price: Double, isPremiumUser: Bool) -> Double {\n        var discount: Double = 0.0\n        \n        if price > 100 {\n            discount += 0.1 // 10% de réduction de base\n        }\n        \n        if isPremiumUser {\n            discount += 0.05 // 5% supplémentaires pour les utilisateurs premium\n        }\n        \n        return price * (1 - discount)\n    }\n}\n
\n\n

Tout semble logique. Écrivons des tests unitaires :

\n\n
// DiscountCalculatorTests.swift\nimport XCTest\n\nfinal class DiscountCalculatorTests: XCTestCase {\n    let calculator = DiscountCalculator()\n    \n    func testDiscountForRegularUserLowPrice() {\n        let result = calculator.calculateDiscount(price: 50, isPremiumUser: false)\n        XCTAssertEqual(result, 50.0, accuracy: 0.01)\n    }\n    \n    func testDiscountForRegularUserHighPrice() {\n        let result = calculator.calculateDiscount(price: 200, isPremiumUser: false)\n        XCTAssertEqual(result, 180.0, accuracy: 0.01)\n    }\n    \n    func testDiscountForPremiumUserLowPrice() {\n        let result = calculator.calculateDiscount(price: 50, isPremiumUser: true)\n        XCTAssertEqual(result, 47.5, accuracy: 0.01)\n    }\n    \n    func testDiscountForPremiumUserHighPrice() {\n        let result = calculator.calculateDiscount(price: 200, isPremiumUser: true)\n        XCTAssertEqual(result, 170.0, accuracy: 0.01)\n    }\n}\n
\n\n

Les tests passent. La couverture est à 100%. Mais voyons ce que le test de mutation a à dire.

\n\

Blogs

Book Recommendations