suspend fun test1(): User {
val profile = repo.getUserProfile()
val friends = repo.getFriends()
return User(profile, friends)
}
suspend fun test2(): User {
val profile = async { repo.getUserProfile() }
val friends = async { repo.getFriends() }
return User(profile.await(), friends.await())
}
getUserProfile
이나 getFriends
가 실행되는데에 적은 시간이 소모된다면 두 함수의 차이를 테스트하기 쉽지 않음.delay
와 같은 지연을 사용해 임의로 실행 기간을 길게 만들어야 함
kotlinx-coroutines-test
라이브러리가 제공하는 StandardTestDispatcher
를 사용해 시간 조작을 할 수 있음kotlinx-coroutines-test
의 TestCoroutineScheduler
는 delay를 가상 시간동안 진행하여 테스트 시 기다리지 않도록 해준다.
아래에서 소개할 advanceTimeBy
, runCurrent
, advanceUntilIdle
과 같은 함수를 제공한다.
fun main() {
val scheduler = TestCoroutineScheduler()
println(scheduler.currentTime) // 0
scheduler.advanceTimeBy(1000)
println(scheduler.currentTime) // 1000
scheduler.advanceTimeBy(1000)
println(scheduler.currentTime) // 2000
}
TestCoroutineScheduler
를 사용하기 위해 보통 StandardTestDispatcher
를 사용한다. 이 디스패처로 시작된 코루틴은 가상 시간만큼 진행되기 전까지 실행되지 않는다.
즉, runTest
에서 테스트 코드를 래핑하면 기본 정지 함수를 테스트할 수 있고 코루틴의 지연을 자동으로 건너뛴다. 따라서 delay
함수나 기타 지연이 생기지 않는다.
private val oneDay = 86400000L
@ExperimentalCoroutinesApi
@Test
fun `기본 테스트 디스패처는 코루틴 스코프를 가장 마지막에 실행한다`() = runTest {
println("test start")
launch {
delay(oneDay)
println("launch 1")
}
async {
println("launch 2")
}
println("test end")
}
위 테스트를 실행해보면 delay
가 있음에도 불구하고 테스트는 바로 종료된다. 결과도 test end가 launch보다 먼저 출력되는 것을 볼 수 있다.
따라서 다음 코드는 원하는 대로 동작을 하지 않는다.
@Test
fun standardTest() = runTest {
val userRepo = UserRepository()
launch { userRepo.register("Alice") }
launch { userRepo.register("Bob") }
assertEquals(listOf("Alice", "Bob"), userRepo.getAllUsers()) // ❌ Fails
}
assertEquals
이 먼저 실행되기 때문에 검증 전 코루틴 스코프를 실행시켜야 한다.