Section 11/171 menit
11. Best Practice: Testing Code Konkuren
11. Best Practice: Testing Code Konkuren
BP 11.1: Test Actor secara Functional
Test method actor lewat public API, bukan internal state.
swift
@Test
func incrementWorks() async {
let counter = Counter()
await counter.increment()
let value = await counter.value
#expect(value == 1)
}
BP 11.2: Stress Test untuk Race Detection
Untuk verifikasi safety di bawah load:
swift
@Test
func incrementsAreAtomic() async {
let c = Counter()
await withTaskGroup(of: Void.self) { group in
for _ in 0..<10_000 {
group.addTask { await c.increment() }
}
}
#expect(await c.value == 10_000)
}
BP 11.3: Mock Clock untuk Deterministic Timing
Jangan pakai Task.sleep di test produksi. Pakai abstraksi Clock.
swift
protocol AppClock: Sendable {
func sleep(for: Duration) async throws
}
struct SystemClock: AppClock {
func sleep(for d: Duration) async throws {
try await Task.sleep(for: d)
}
}
// Test pakai mock yang resolve instan
struct InstantClock: AppClock {
func sleep(for: Duration) async throws { }
}
BP 11.4: Hindari Task.sleep di Test sebagai "Tunggu"
Anti-pattern paling umum: try await Task.sleep(for: .seconds(1)) untuk "tunggu async selesai". Itu flaky.
swift
// ❌ Flaky
@Test
func updatesViewModel() async {
let vm = ViewModel()
Task { await vm.load() }
try await Task.sleep(for: .seconds(1)) // tebak-tebakan
#expect(vm.isLoaded)
}
// ✓ Await langsung
@Test
func updatesViewModel() async {
let vm = ViewModel()
await vm.load()
#expect(vm.isLoaded)
}
BP 11.5: Test MainActor View dengan @MainActor Suite
Untuk test ViewModel @MainActor, tandai test suite @MainActor juga.
swift
@Suite("HomeViewModel", .serialized)
@MainActor
struct HomeViewModelTests {
@Test
func loadShowsLoadingThenItems() async {
let vm = HomeViewModel(service: MockService())
#expect(vm.items.isEmpty)
await vm.load()
#expect(vm.items.count == 3)
}
}