web-dev-qa-db-ja.com

kotlinコルーチンを使用する場合、サスペンド関数を呼び出す関数を単体テストするにはどうすればよいですか?

このようなクラスがあります

_class SomeClass {
    fun someFun() {
        // ... Some synchronous code
        async {
            suspendfun() 
        }
    }

    private suspend fun suspendFun() {
         dependency.otherFun().await()
         // ... other code
    }
}
_

someFun()を単体テストしたいので、次のような単体テストを作成しました。

_@Test
fun testSomeFun() {
    runBlocking {
        someClass.someFun()
    }

    // ... verifies & asserts
}
_

しかし、runBlockingは実際にはrunBlocking内のすべてが完了するまで実行をブロックしないため、これは機能していないようです。 suspendFun()runBlocking内で直接テストすると、期待どおりに動作しますが、someFun()をすべて一緒にテストできるようにしたいと思います。

同期コードと非同期コードの両方で関数をテストする方法の手がかりはありますか?

21
user1809913

非同期の修正

実装されているように、あなたのsomeFun()asyncの結果を「発射して忘れる」だけです。その結果、runBlockingはそのテストで違いを生じません。

可能であれば、someFun()asyncDeferredを返し、runBlockingawaitを呼び出します。

fun someFun(): Deferred<Unit> {
    // ... Some synchronous code
    return async {
        suspendFun()
    }
}

次に、テスト:

runBlocking {
    SomeClass().someFun().await()
}

この question/answer は、詳細な情報を得るための優れたリソースです。

代替:起動を使用

async関数とsuspendで作成されたコルーチンを使用して、launchを避けることもできます。

suspend fun someFun() {
    // ... Some synchronous code
    suspendFun()
}

private suspend fun suspendFun() {
    delay(1000)
    println("executed")
    // ... other code
}

テストはlaunchを使用し、外側のrunBlockingはその完了を暗黙的に待機します。

val myScope = GlobalScope
runBlocking {
    myScope.launch {
        SomeClass().someFun()
    }
}
13
s1m0nw1