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)
}