Section 5/161 menit

5. Confirmation: Testing Callback-Based Code

5. Confirmation: Testing Callback-Based Code

Confirmation adalah cara formal Swift Testing untuk testing kode yang berbasis callback, delegate, atau event-driven.

Sintaks Dasar Confirmation

swift
import Testing

// Test bahwa callback dipanggil tepat 1 kali (default)
@Test
func testCompletionCalled() async {
    let service = DataService()

    await confirmation("Completion handler dipanggil") { confirm in
        service.fetchData { result in
            #expect(result != nil)
            confirm()  // Tandai bahwa expected event terjadi
        }
    }
}

// Test bahwa callback dipanggil tepat N kali
@Test
func testProgressCallbacks() async {
    let uploader = FileUploader()
    let expectedCallCount = 5  // Upload 5 file, 5 progress update

    await confirmation("Progress dipanggil 5 kali", expectedCount: expectedCallCount) { confirm in
        uploader.uploadBatch(files: generateFiles(count: 5)) { progress in
            confirm()  // Dipanggil setiap ada progress update
        }
    }
}

// Test bahwa event TIDAK terjadi (expectedCount: 0)
@Test
func testNoErrorCallbackOnSuccess() async {
    let service = RobustService()

    await confirmation("Error callback tidak dipanggil", expectedCount: 0) { confirm in
        service.performOperation(
            onSuccess: { /* valid path */ },
            onError: { error in
                confirm()  // Jika ini dipanggil, test gagal (karena expectedCount: 0)
            }
        )
        // Tunggu sebentar untuk memastikan error tidak muncul
        try? await Task.sleep(nanoseconds: 100_000_000)
    }
}

Confirmation untuk Delegate Pattern

swift
// Testing delegate yang async
protocol DownloadDelegate: AnyObject {
    func downloadDidStart()
    func downloadDidProgress(fraction: Double)
    func downloadDidComplete(data: Data)
    func downloadDidFail(error: Error)
}

@Test
func testDelegateCallSequence() async {
    class TestDelegate: DownloadDelegate {
        var onStart: (() -> Void)?
        var onProgress: ((Double) -> Void)?
        var onComplete: ((Data) -> Void)?

        func downloadDidStart() { onStart?() }
        func downloadDidProgress(fraction: Double) { onProgress?(fraction) }
        func downloadDidComplete(data: Data) { onComplete?(data) }
        func downloadDidFail(error: Error) { Issue.record("Unexpected failure: \(error)") }
    }

    let delegate = TestDelegate()
    let downloader = FileDownloader()
    downloader.delegate = delegate

    // Verifikasi urutan: start → progress (beberapa kali) → complete
    await confirmation("Download dimulai") { confirmStart in
        await confirmation("Download selesai") { confirmComplete in
            delegate.onStart = { confirmStart() }
            delegate.onComplete = { data in
                #expect(!data.isEmpty)
                confirmComplete()
            }

            await downloader.download(url: URL(string: "https://example.com/file")!)
        }
    }
}

Confirmation untuk NotificationCenter

swift
// Testing bahwa notifikasi tertentu di-post
@Test
func testAuthStateChangedNotification() async {
    let authManager = AuthManager()

    await confirmation("Auth state notification di-post") { confirm in
        let observer = NotificationCenter.default.addObserver(
            forName: .authStateChanged,
            object: nil,
            queue: nil
        ) { notification in
            #expect(notification.userInfo?["isLoggedIn"] as? Bool == true)
            confirm()
        }
        defer { NotificationCenter.default.removeObserver(observer) }

        await authManager.signIn(username: "test", password: "pass")
    }
}