Unit Test vs Integration Test Karşılaştırması

Hız, izolasyon ve güvenilir geri bildirim döngüsü

VS
Integration Test

Gerçek bileşen etkileşimi, uçtan uca doğrulama

8 dk okumaiOS

Puan Karşılaştırması

Grafik yükleniyor...

Detaylı Puanlama

Performans
Unit Test10/10
Integration Test4/10
Öğrenme Kolaylığı
Unit Test8/10
Integration Test6/10
Ekosistem
Unit Test9/10
Integration Test8/10
Topluluk
Unit Test9/10
Integration Test8/10
İş Pazarı
Unit Test9/10
Integration Test8/10
Gelecek
Unit Test9/10
Integration Test9/10

Artıları & Eksileri

Unit Test

Artıları

  • Son derece hızlı — milisaniyeler içinde çalışıyor, CI'da saniyelerde
  • İzole — dış bağımlılıklar mock/stub ile kontrol altında
  • Güvenilir — flaky test riski minimum (network, DB yok)
  • Regresyon tespiti — küçük kod değişikliklerini anında yakalıyor
  • Dokümantasyon — iyi yazılmış unit testler kodun nasıl kullanılacağını gösteriyor
  • TDD ile tasarım kalitesini yükseltme
  • Parallel çalışabilme — çok core'u verimli kullanma

Eksileri

  • Mock'lar gerçeği tam yansıtmayabilir — sahte pozitif/negatif riski
  • Entegrasyon sorunlarını tespit edemiyor — sıkça 'unit testler geçti ama üretim patladı' senaryosu
  • Over-mocking tehlikesi — testler implementation'a sıkıca bağlanabilir
  • Private metodları test etmek için tasarım değişikliği gerekebilir
  • Birbirinden bağımsız modüllerin gerçek dünyadaki uyumunu göstermiyor

En Uygun

İş mantığı (business logic) doğrulamaSaf fonksiyon ve algoritma testleriViewModel/Reducer state geçişleriParser, formatter, validator testleriKütüphane ve framework geliştirme

Integration Test

Artıları

  • Gerçek bileşen etkileşimini doğruluyor — mock yanılgısı yok
  • Entegrasyon hatalarını tespit ediyor — unit'lerin birlikte çalışmasını test ediyor
  • Database, network katmanlarını gerçek olarak test etme imkânı
  • Kritik akışları uçtan uca doğrulama
  • Refactoring güvencesi — interface değişikliklerini yakalıyor
  • Kullanıcı senaryolarına daha yakın test

Eksileri

  • Yavaş — network, database, dosya sistemi gerçek zamanlı
  • Flaky test riski — dış servisler güvenilmez olabilir
  • Kurulum ve teardown karmaşıklığı
  • Başarısız testlerin nedenini bulmak zor
  • Paralel çalıştırma state çakışmaları yaratabiliyor
  • Mock olmayan ortam için ek altyapı gerekiyor

En Uygun

Database CRUD operasyonları (Core Data, SwiftData, SQLite)API istemci katmanı doğrulamaBirden fazla servisin birlikte çalışmasıCritical path (ödeme, auth akışı) doğrulamaVeri migration ve transformation testleri

Kod Karşılaştırması

Unit Test
// Swift - Unit test örnekleri (XCTest + Swift Testing)
import Testing
import Foundation
@testable import MyApp

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

    @Test("Türk lirası formatlaması doğru olmalı")
    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("Geçersiz fiyat negatif olmamalı",
          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("Ürün eklenince toplam fiyat güncellenmeli")
    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("Aynı ürün iki kez eklenince miktar artmalı")
    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)
    }
}

// Mock implementasyonu
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 - Integration test örnekleri
import XCTest
@testable import MyApp

// In-memory SQLite ile integration test
class UserRepositoryIntegrationTests: XCTestCase {
    var repository: UserRepository!
    var database: TestDatabase!

    override func setUp() async throws {
        // Gerçek SQLite in-memory database kullanıyoruz
        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 {
        // Create
        let userId = try await repository.createUser(
            name: "Ahmet Yılmaz",
            email: "[email protected]"
        )

        // Fetch
        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)

        // Kullanıcıyı sil
        try await repository.deleteUser(id: userId)

        // Kullanıcının postları da silinmeli (cascade)
        let posts = try await postRepo.fetchPosts(userId: userId)
        XCTAssertTrue(posts.isEmpty, "Kullanıcı silinince postları da silinmeli")
    }
}

// Gerçek URLSession ile network integration test
class APIClientIntegrationTests: XCTestCase {
    func testFetchPublicAPIData() async throws {
        // Bu test gerçek ağ bağlantısı kullanıyor
        // CI'da sadece network bağlantılı ortamlarda çalıştırılmalı
        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)
    }
}

Sonuç

Test piramidi: çok sayıda unit test (%70), az sayıda integration test (%20), minimum UI/E2E test (%10). Unit testler hızlı geri bildirim için, integration testler kritik akışları doğrulamak için birlikte kullanın. Birini diğerinin yerine koymak değil, tamamlayıcı olarak kullanmak doğru strateji.

SSS

Sıkça Sorulan Sorular

Sihirli bir rakam yok. %80+ iş mantığı coverage'ı iyi bir hedef. Ancak coverage yüzdesi değil, doğru şeyleri test etmek önemli. %100 coverage'a körü körüne ulaşmaya çalışmak zararlı.

İlgili Blog Yazıları

Tüm Yazıları Gör

İlgili Projeler

Tüm Projeleri Gör

Bunu da begenebilirsiniz