web-dev-qa-db-ja.com

遅延を伴うKotlinコルーチンのユニットテスト

私はdelay()を使用するKotlinコルーチンを単体テストしようとしています。単体テストの場合、delay()は気にしません。テストが遅くなるだけです。 delay()が呼び出されたときに実際に遅延しない方法でテストを実行したいと思います。

CommonPoolに委任するカスタムコンテキストを使用してコルーチンを実行してみました。

_class TestUiContext : CoroutineDispatcher(), Delay {
    suspend override fun delay(time: Long, unit: TimeUnit) {
        // I'd like it to call this
    }

    override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
        // but instead it calls this
    }

    override fun dispatch(context: CoroutineContext, block: Runnable) {
        CommonPool.dispatch(context, block)
    }
}
_

コンテキストのdelay()メソッドから戻るだけでいいのにと思っていましたが、代わりにscheduleResumeAfterDelay()メソッドを呼び出しており、それをデフォルトのスケジューラーに委任する方法がわかりません。

29
Erik Browne

Kotlinx.coroutines v1.2.1では、 kotlinx-coroutines-test モジュールが追加されました。 runBlockingTestコルーチンビルダー、およびTestCoroutineScopeTestCoroutineDispatcherが含まれています。これらは自動進行時間を可能にするだけでなく、delayを使用してコルーチンをテストする時間を明示的に制御します。

0
Erik Browne

遅延を望まない場合は、スケジュールコールの継続を単純に再開してみませんか。

_class TestUiContext : CoroutineDispatcher(), Delay {
    override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
        continuation.resume(Unit)
    }

    override fun dispatch(context: CoroutineContext, block: Runnable) {
        //CommonPool.dispatch(context, block)  // dispatch on CommonPool
        block.run()  // dispatch on calling thread
    }
}
_

そうすれば、delay()は遅延なく再開されます。これはまだ遅延して中断するため、他のコルーチンは引き続き実行できます(yield()など)。

_@Test
fun `test with delay`() {
    runBlocking(TestUiContext()) {
        launch { println("launched") }
        println("start")
        delay(5000)
        println("stop")
    }
}
_

遅延なく実行され、印刷されます。

_start
launched
stop
_

編集:

dispatch関数をカスタマイズして、継続が実行される場所を制御できます。

18
bj0

Kotlinx.coroutines v0.23.0で TestCoroutineContext が導入されました。

プロ:delayを使用してコルーチンを真にテストできます。 CoroutineContextの仮想クロックを特定の時間に設定して、予想される動作を確認できます。

欠点:コルーチンコードでdelayを使用せず、呼び出しスレッドで同期的に実行するだけの場合、@ bj0の回答のTestUiContextよりも使用するのが少し面倒です(コルーチンを実行するには、TestCoroutineContextでtriggerActions()を呼び出す必要があります)。

補足:TestCoroutineContextkotlinx-coroutines-testモジュールはコルーチンバージョン1.2.1以降で、非推奨とマークされるか、このバージョンより上のバージョンの標準コルーチンライブラリには存在しません。

3
Erik Browne