Section 7/161 menit

7. Testing Async Code dan Concurrency

7. Testing Async Code dan Concurrency

Testing Actor-Isolated Code

swift
import Testing

actor Counter {
    private(set) var value = 0

    func increment() { value += 1 }
    func reset() { value = 0 }
}

// Test actor langsung — method actor adalah async secara implisit
@Test
func testCounterIncrement() async {
    let counter = Counter()
    await counter.increment()
    await counter.increment()
    let value = await counter.value
    #expect(value == 2)
}

// Test bahwa actor safely handles concurrent access
@Test
func testConcurrentIncrement() async {
    let counter = Counter()
    let incrementCount = 100

    // Jalankan banyak increment secara paralel
    await withTaskGroup(of: Void.self) { group in
        for _ in 0..<incrementCount {
            group.addTask {
                await counter.increment()
            }
        }
    }

    let finalValue = await counter.value
    #expect(finalValue == incrementCount)  // Actor menjamin tidak ada lost updates
}

Testing AsyncStream

swift
@Test
func testTemperatureStream() async throws {
    let sensor = TemperatureSensor()
    var readings: [Double] = []

    let stream = sensor.temperatureStream()

    // Ambil 5 readings lalu stop
    for try await temperature in stream.prefix(5) {
        readings.append(temperature)
    }

    #expect(readings.count == 5)
    #expect(readings.allSatisfy { $0 > -50 && $0 < 100 })
}

Testing dengan Task Cancellation

swift
@Test
func testOperationRespectsCancellation() async {
    let operation = LongRunningOperation()
    var wasCancelled = false

    let task = Task {
        do {
            try await operation.execute()
        } catch is CancellationError {
            wasCancelled = true
        }
    }

    // Beri sedikit waktu untuk mulai
    try? await Task.sleep(nanoseconds: 100_000_000)

    task.cancel()

    // Tunggu task selesai
    await task.value

    #expect(wasCancelled, "Operation harus menghormati cancellation")
}

Testing Clock-Dependent Code

swift
// Gunakan custom Clock untuk test yang deterministik
@Test
func testRetryWithDelay() async throws {
    var callCount = 0
    let testClock = TestClock()  // Custom clock yang bisa di-advance manual

    let retrier = Retrier(clock: testClock)
    retrier.onAttempt = { callCount += 1; throw NetworkError.timeout }

    // Advance clock tanpa benar-benar menunggu
    async let retryTask = retrier.retry(maxAttempts: 3, delay: .seconds(1))
    await testClock.advance(by: .seconds(3))

    #expect(callCount == 3)
}