Unit Test vs Integration Test Comparaison

Vitesse, isolation et boucle de feedback fiable

VS
Integration Test

Interaction réelle des composants, validation de bout en bout

8 min de lectureiOS

Comparaison des scores

Chargement du graphique...

Notation détaillée

Notation détaillée: Unit Test et Integration Test — notes sur 10, catégorie par catégorie
CatégorieUnit TestIntegration Test
Performance
10/10
4/10
Facilité d'apprentissage
8/10
6/10
Écosystème
9/10
8/10
Communauté
9/10
8/10
Marché de l'emploi
9/10
8/10
Pérennité
9/10
9/10

Avantages & Inconvénients

Unit Test

Avantages

  • Extrêmement rapide — s'exécute en millisecondes, en secondes en CI
  • Isolé — les dépendances externes sont contrôlées via mock/stub
  • Fiable — risque minimal de test flaky (pas de réseau, pas de DB)
  • Détection de régression — repère instantanément les petits changements de code
  • Documentation — des tests unitaires bien écrits montrent comment utiliser le code
  • Améliore la qualité de conception via le TDD
  • Exécution parallèle possible — utilisation efficace du multi-cœur

Inconvénients

  • Les mocks peuvent ne pas refléter fidèlement la réalité — risque de faux positifs/négatifs
  • Ne détecte pas les problèmes d'intégration — scénario fréquent où « les tests unitaires passent mais la prod explose »
  • Danger de l'over-mocking — les tests peuvent devenir trop couplés à l'implémentation
  • Tester des méthodes privées peut nécessiter de modifier la conception
  • Ne démontre pas la compatibilité réelle entre modules indépendants

Idéal pour

Validation de la logique métierTests de fonctions pures et d'algorithmesTransitions d'état ViewModel/ReducerTests de parser, formatter, validatorDéveloppement de bibliothèques et frameworks

Integration Test

Avantages

  • Valide l'interaction réelle des composants — pas d'illusion de mock
  • Détecte les bugs d'intégration — teste si les unités fonctionnent réellement ensemble
  • Permet de tester les couches base de données et réseau dans des conditions réelles
  • Validation de bout en bout des flux critiques
  • Sécurité pour le refactoring — détecte les changements d'interface
  • Tests plus proches des scénarios utilisateurs réels

Inconvénients

  • Lent — réseau, base de données, système de fichiers en temps réel
  • Risque de tests flaky — les services externes peuvent être peu fiables
  • Complexité de la mise en place et du nettoyage (teardown)
  • Difficile d'identifier la cause d'un test en échec
  • L'exécution parallèle peut créer des conflits d'état
  • Nécessite une infrastructure supplémentaire pour un environnement sans mock

Idéal pour

Opérations CRUD en base de données (Core Data, SwiftData, SQLite)Validation de la couche client APIFonctionnement conjoint de plusieurs servicesValidation des chemins critiques (paiement, flux d'authentification)Tests de migration et de transformation de données

Comparaison de code

Unit Test
// Swift - Exemples de tests unitaires (XCTest + Swift Testing)
import Testing
import Foundation
@testable import MyApp

// Framework Swift Testing (iOS 17+, WWDC 2024)
struct PriceFormatterTests {

    @Test("Le formatage de la livre turque doit être correct")
    func turkishLiraFormat() {
        let formatter = PriceFormatter(locale: Locale(identifier: "tr_TR"))
        #expect(formatter.format(1234.5) == "₺1.234,50")
        #expect(formatter.format(0) == "₺0,00")
        #expect(formatter.format(-50) == "-₺50,00")
    }

    @Test("Un prix invalide ne doit pas être négatif",
          arguments: [-1.0, -100.0, -0.01])
    func negativePriceValidation(price: Double) {
        let validator = PriceValidator()
        #expect(!validator.isValid(price))
    }
}

struct CartViewModelTests {
    var sut: CartViewModel!
    var mockRepository: MockCartRepository!

    @Test("Le prix total doit être mis à jour quand un produit est ajouté")
    mutating func addProductUpdatesTotalPrice() async throws {
        mockRepository = MockCartRepository()
        sut = CartViewModel(repository: mockRepository)

        let product = Product(id: "p1", name: "MacBook", price: 75000)
        await sut.addProduct(product)

        #expect(sut.totalPrice == 75000)
        #expect(sut.itemCount == 1)
    }

    @Test("La quantité doit augmenter quand le même produit est ajouté deux fois")
    mutating func addSameProductIncreasesQuantity() async throws {
        mockRepository = MockCartRepository()
        sut = CartViewModel(repository: mockRepository)

        let product = Product(id: "p1", name: "MacBook", price: 75000)
        await sut.addProduct(product)
        await sut.addProduct(product)

        #expect(sut.items.count == 1)
        #expect(sut.items.first?.quantity == 2)
        #expect(sut.totalPrice == 150000)
    }
}

// Implémentation du mock
class MockCartRepository: CartRepositoryProtocol {
    var savedItems: [CartItem] = []

    func save(_ item: CartItem) async throws {
        savedItems.append(item)
    }

    func fetchAll() async throws -> [CartItem] {
        return savedItems
    }
}
Integration Test
// Swift - Exemples de tests d'intégration
import XCTest
@testable import MyApp

// Test d'intégration avec SQLite en mémoire
class UserRepositoryIntegrationTests: XCTestCase {
    var repository: UserRepository!
    var database: TestDatabase!

    override func setUp() async throws {
        // On utilise une vraie base de données SQLite en mémoire
        database = try await TestDatabase.inMemory()
        repository = UserRepository(database: database)
    }

    override func tearDown() async throws {
        try await database.cleanup()
        database = nil
        repository = nil
    }

    func testCreateAndFetchUser() async throws {
        // Création
        let userId = try await repository.createUser(
            name: "Ahmet Yılmaz",
            email: "[email protected]"
        )

        // Récupération
        let fetchedUser = try await repository.fetchUser(id: userId)

        XCTAssertNotNil(fetchedUser)
        XCTAssertEqual(fetchedUser?.name, "Ahmet Yılmaz")
        XCTAssertEqual(fetchedUser?.email, "[email protected]")
    }

    func testDeleteUserCascadesToPosts() async throws {
        let userId = try await repository.createUser(name: "Test", email: "[email protected]")
        let postRepo = PostRepository(database: database)
        _ = try await postRepo.createPost(title: "Post 1", userId: userId)
        _ = try await postRepo.createPost(title: "Post 2", userId: userId)

        // Supprimer l'utilisateur
        try await repository.deleteUser(id: userId)

        // Les posts de l'utilisateur doivent aussi être supprimés (cascade)
        let posts = try await postRepo.fetchPosts(userId: userId)
        XCTAssertTrue(posts.isEmpty, "La suppression de l'utilisateur doit aussi supprimer ses posts")
    }
}

// Test d'intégration réseau avec une vraie URLSession
class APIClientIntegrationTests: XCTestCase {
    func testFetchPublicAPIData() async throws {
        // Ce test utilise une vraie connexion réseau
        // Ne doit s'exécuter que dans des environnements CI avec accès réseau
        try XCTSkipUnless(ProcessInfo.processInfo.environment["INTEGRATION_TESTS"] == "1")

        let client = APIClient(baseURL: URL(string: "https://jsonplaceholder.typicode.com")!)
        let posts: [Post] = try await client.request(path: "/posts")
        XCTAssertFalse(posts.isEmpty)
        XCTAssertEqual(posts.count, 100)
    }
}

Conclusion

Pyramide de tests : beaucoup de tests unitaires (70%), moins de tests d'intégration (20%), un minimum de tests UI/E2E (10%). Les tests unitaires offrent un retour rapide, les tests d'intégration valident les flux critiques — utilisez-les ensemble. La bonne stratégie n'est pas de remplacer l'un par l'autre, mais de les considérer comme complémentaires.

Obtenir une consultation gratuite
FAQ

Questions fréquentes

Il n'y a pas de chiffre magique. Une couverture de 80%+ de la logique métier est un bon objectif. Mais ce qui compte, ce n'est pas le pourcentage, c'est de tester les bonnes choses. Chercher aveuglément 100% de couverture peut être contre-productif.

Articles de blog associés

Voir tous les articles

Projets associés

Voir tous les projets
Toutes les comparaisons