테스트에서 정지함수를 호출하려면 코루틴이 있어야 하는데, JUnit 테스트 함수 자체는 정지함수가 아니다. 따라서 테스트 내에 코루틴 빌더를 이용하여, 새로운 코루틴을 생성한다.
runTest에서 테스트 코드를 래핑하면 기본 정지 함수를 테스트할 수 있고 코루틴의 지연을 자동으로 건너뛴다.
@Test
fun `delay는 자동으로 skip된다`() = runTest {
println("before test")
launch {
delay(5000)
println("launch1")
}
launch {
println("launch2")
}
println("after delay")
}
그러나 테스트 중인 코드에서 발생하는 상황에 따라 추가로 고려해야 할 사항이 있다.
runTest
에서 만드는 최상위 코루틴 외에 새 코루틴을 만들 때는 적절한 TestDispatcher를 선택하여 새 코루틴이 예약되는 방식을 제어해야 한다.withContext
이용)하면 runTest는 일반적으로 계속 작동하지만 지연을 더 이상 건너뛰지 않으며 테스트 코드가 여러 스레드에서 실행되므로 예측 가능성이 떨어진다.테스트 디스패처는 테스트 목적으로 사용하는 CoroutineDispatcher
의 구현이다. 새 코루틴의 실행을 예측할 수 있도록 테스트 중에 새 코루틴을 만드는 경우 TestDispatchers를 사용해야 한다.
테스트에서 사용하는 스케줄러 인스턴스는 하나만 있어야 하며, 모든 TestDispatchers 간에 공유되어야 한다. runTest
는 테스트 코루틴을 시작하기 위해 TestScope
를 만든다. TestDispatcher를 지정하지 않으면, TestScope는 기본적으로 StandardTestDispatcher
를 만들고 이를 사용하여 최상위 코루틴을 실행한다.
StandardTestDispatcher
에서 새 코루틴을 시작하면 코루틴이 기본 스케줄러의 대기열에 추가되어 테스트 스레드를 사용할 수 있을 때마다 실행된다. 이러한 새 코루틴이 실행되도록 하려면 테스트 스레드를 생성해야 한다.
테스트 스레드가 최상위 테스트 코루틴(runTest)을 실행하는 동안 생성되지 않으면, 모든 새 코루틴은 테스트 코루틴이 완료된 후(하지만 runTest가 반환되기 전이다)에만 실행된다.
@Test
fun standardTest() = runTest {
val userRepo = UserRepository()
launch { userRepo.register("Alice") }
launch { userRepo.register("Bob") }
assertEquals(listOf("Alice", "Bob"), userRepo.getAllUsers()) // ❌ Fails
}
대기열에 추가된 코루틴이 실행되도록 테스트 코루틴을 생성하는 방법에는 여러가지가 있다.