PROGRAMMING LANGUAGE/KOTLIN

[Kotlin Coroutine] kotlinx-coroutines-test를 활용한 coroutine 테스트

EARTH_ROOPRETELCHAM 2024. 6. 29. 13:26
728x90
반응형

들어가기 전에

coroutine 스터디를 진행하면서, 매 실습마다 main()에서 돌게 하기 위해 변경해야 하는 점이 불편했습니다.

이에 coroutine용 테스트에 대해 알아보게 되었고, kotlinx-coroutines-test라는 jetbrains에서 제공해주는 테스트 모듈이 있다는 것을 알게되어 해당 내용을 정리해보고자 합니다.

kotlinx-coroutines-test

프로젝트에 kotlinx-coroutins-test 추가하기

dependencies {
    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:[자신의 kotlinx-coroutines 버전]'
}
  • 이 모듈은 테스트를 위한 유틸리티만을 제공하기 때문에, test에서만 사용되도록 testImplementation을 걸어야 합니다.

무엇을 제공해줄까?

kotlinx-coroutines-test는 kotlinx.coroutines를 위한 테스트 유틸리티로, 제공해주는 테스트용 유틸리티는 아래와 같습니다.

runTest

    @Test
    fun structuredConcurrency() = runTest {
        val coroutineContext = newSingleThreadContext("myThread") + CoroutineName("MyCoroutine")
        launch(coroutineContext) {
            println("[${Thread.currentThread().name}] 부모 코루틴 실행")
            launch {
                println("[${Thread.currentThread().name}] 자식 코루틴 실행")
            }
        }
    }

runTestrunBlocking에서 코드를 실행하는 것과 유사한 역할을 하며, 주된 차이점은 아래와 같습니다.

  • task 수행 순서를 보존하면서, delay 호출이 자동으로 스킵됩니다. 따라서, 테스트를 빠르게 수행할 수 있습니다.
  • 60초가 넘으면 테스트 수행이 취소되어 CI 리소스를 사용해 계속 테스트 코루틴이 살아있는 것을 방지합니다.
  • 가상 시간을 컨트롤하여 시간과 관련된 테스트할 때 도움을 줍니다.
  • uncaught exception을 테스트가 끝날 때 핸들링합니다.
  • 비동기적 콜백을 기다립니다. runTest는 일부 코드가 테스트 모듈과 통합되지 않은 디스패처에서 실행되는 상황을 처리합니다.

delay-skipping

runTest 블록 내에서 테스트를 수행하면, delay 로직을 스킵하고 테스트를 진행할 수 있습니다.

10초를 기다리지 않고 38ms만에 테스트 종료

launch와 async

runTest의 coroutine dispatcher는 싱글 스레드를 사용하기 때문에, runTest 블록이 실행된 스레드에서 launchasync 테스트가 모두 이루어지며, 병렬적으로 수행되지 않습니다.

여러 코루틴이 delay가 걸려있는 경우, 가장 delay가 작은 예약 코루틴이 선택되어 실행되며, 가상 시간은 자동으로 재개 시점으로 진행됩니다.

단일 스레드에서 테스트가 동작하며, delay는 무시하되 코루틴 실행 순서를 유지할 수 있도록 함

virtual time 컨트롤

runTest 내에서는 TestCoroutineScheduler에 따라 코루틴 수행이 스케줄링됩니다. TestCoroutineScheduler는 가상 시간 스케줄러로 아래와 같은 특별한 메소드들을 제공해주며, 가상 시간을 컨트롤할 수 있게 해줍니다.

  • currentTime: 현재 virtual time 반환
  • runCurrent(): 현 virtual time에 수행되어야 할 작업을 실행
  • advanceUntilIdle(): 현재 큐잉하고 있는 모든 작업들 실행
  • advanceTimeBy(timeDelta): 특정 virtual time까지 일어나야 할 큐잉된 작업들을 실행
  • timeSource: virtual time을 사용하는 TimeSource 반환

가상 시간 예제

참고 자료

 

728x90
반응형