Kotest 코루틴(Coroutines)
코루틴 디스패처 테스트
TestDispatcher
는 개발자 가 가상 시계를 제어하고 지연을 건너뛸 수 있도록 하는 kotlinx-coroutines-test
모듈 에서 제공하는 특수 CoroutineDispatcher
이다.
TestDispatcher
는 개발자가 가상 클럭을 제어하고 지연을 건너뛸 수 있도록 하는 kotlinx-coroutines-test 모듈에서 제공하는 특수 CoroutineDispatcher이다.
TestDispatcher
는 다음 작업을 지원한다.
currentTime
은 현재 가상 시간을 가져온다.runCurrent()
는 이 가상 시간 시점에 예약된 작업을 실행한다.advanceUntilIdle()
은 더 이상 대기 중인 작업이 없을 때까지 대기 중인 모든 작업을 실행한다.advanceTimeBy(timeDelta)
는 현재 가상 시간이 timeDelta만큼 앞당겨질 때까지 큐에 대기 중인 작업을 실행한다.
테스트에 TestDispatcher
를 사용하려면 테스트 구성에서 coroutineTestScope
를 활성화하면 된다:
package com.devkuma.kotest.tutorial.coroutines.ex1
import io.kotest.core.spec.style.FunSpec
class TestDispatcherTest : FunSpec() {
init {
test("foo").config(coroutineTestScope = true) {
// this test will run with a test dispatcher
}
}
}
이 테스트 내부에서 testCoroutineScheduler
라는 Extension를 통해 스케줄러에 대한 핸들을 검색할 수 있다. 이 스케줄러를 사용하여 시간을 조작할 수 있다:
import io.kotest.core.test.testCoroutineScheduler
class TestDispatcherTest : FunSpec() {
init {
test("advance time").config(coroutineTestScope = true) {
val duration = 1.days
// launch a coroutine that would normally sleep for 1 day
launch {
delay(duration.inWholeMilliseconds)
}
// move the clock on and the delay in the above coroutine will finish immediately.
testCoroutineScheduler.advanceTimeBy(duration.inWholeMilliseconds)
val currentTime = testCoroutineScheduler.currentTime
}
}
}
스펙 레벨에서 coroutineTestScope
를 true로 설정하여 스펙의 모든 테스트에 대해 테스트 디스패처를 활성화할 수 있다:
class TestDispatcherTest : FunSpec() {
init {
coroutineTestScope = true
test("this test uses a test dispatcher") {
}
test("and so does this test!") {
}
}
}
마지막으로 ProjectConfig
를 사용하여 모듈의 모든 테스트에 대해 테스트 디스패처를 활성화할 수 있다:
class ProjectConfig : AbstractProjectConfig() {
override var coroutineTestScope = true
}
코루틴 디버깅
kotlinx-coroutines-debug는 JVM에서 코루틴에 대한 디버깅 기능을 제공하는 모듈이다. 활성화하면 디버그 에이전트가 ByteBuddy에 의해 설치되고 코루틴이 생성, 시작, 일시 중단 및 다시 시작될 때 정보를 캡처한다.
Kotest는 테스트별로 디버깅을 활성화할 수 있는 기능을 제공한다. 테스트 구성에서 coroutineDebugProbe
를 활성화하면 이 기능을 사용할 수 있다.
이 기능을 활성화하면 테스트 내에서 시작된 모든 코루틴은 테스트 완료 후 또는 예외가 발생하는 즉시 ‘coroutine dump’에 포함된다.
class CoroutineDebugging : FunSpec() {
init {
test("foo").config(coroutineDebugProbes = true) {
someMethodThatLaunchesACoroutine() // launches a new coroutine
}
}
}
‘coroutine dump’는 다음과 같다.
Coroutines dump 2021/11/27 22:17:43
Coroutine DeferredCoroutine{Active}@71f1906, state: CREATED
(Coroutine creation stacktrace)
at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:122)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.async$default(Builders.common.kt:82)
at kotlinx.coroutines.BuildersKt.async$default(Unknown Source)
at com.sksamuel.kotest.engine.coroutines.Wibble$1.invokeSuspend(CoroutineDebugTest.kt:37)
at com.sksamuel.kotest.engine.coroutines.Wibble$1.invoke(CoroutineDebugTest.kt)
스펙 레벨 구성
스펙 내에서 coroutineDebugProbes
설정을 재정의하여 스펙의 모든 테스트에 대해 코루틴 디버깅을 활성화할 수 있다:
class CoroutineDebugging : FunSpec() {
init {
coroutineDebugProbes = true
test("foo") {
// debugging enabled here
}
test("bar") {
// debugging enabled here
}
}
}
프로젝트 전체 구성
ProjectConfig
를 사용하여 프로젝트의 모든 테스트에 대해 코루틴 디버깅을 활성화할 수 있다:
class ProjectConfig : AbstractProjectConfig() {
override val coroutineDebugProbes = true
}