スポンサーリンク

Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used

スポンサーリンク

原因

Main Threadでの実行が必要なAPIを使用している。

対策

公式の回答がこちら

Testing Kotlin coroutines on Android  |  Android Developers

DispatcherをMainに置き換える

基本的にこちらの方法で問題ありません。ベストプラクティスで多く説明されている通り、Domain層やData層のクラスではDispatcherをInjectできる形式で実装していると思います。

Best practices for coroutines in Android  |  Kotlin  |  Android Developers

それらのDispatcherに対して、Unit TestではDispatcher.Mainを渡せばOKです。

テスト実行環境のMainをTestDispatcherに置き換える

上記の対策が使用できないのがViewModel層において、viewModelScope extentionを使用している場合です。”viewModelScope”がハードコードされているため、dispatcherの置き換えができません。この場合、Dispatchers.setMain()を利用して、明示的にMain DispatcherをTest Dispatcherに置き換えます。

Dispatchers.setMain( UnconfinedTestDispatcher(testScheduler))
//some test codes
Dispatchers.resetMain()

これをすべてのTestごとに記載するのは手間なので、JUnit4であれば@Rule、JUnit5であればExtensionにまとめます。JUnit4の例は上記の公式に記載がありますので、以下にJUnit5の場合の例を記載します。

以下ではMockitoExtensionと作成したMainDispatcherExtensionを併用しています。

@OptIn(ExperimentalCoroutinesApi::class)
class MainDispatcherExtension(
    private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) : BeforeEachCallback, AfterEachCallback{
    override fun beforeEach(context: ExtensionContext?) {
        Dispatchers.setMain(testDispatcher)
    }

    override fun afterEach(context: ExtensionContext?) {
        Dispatchers.resetMain()
    }
}

@ExtendWith(MockitoExtension::class, MainDispatcherExtension::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
internal class ListSuggestionViewModelTest{
    @BeforeEach
    fun setUp() = runTest{

    }

    @Test
    fun test1() = runTest {

    }
}

はまりポイント

Kotlinにおける複数の@ExtendWithの記載方法

 @ExtendWith(MockitoExtension::class, MainDispatcherExtension::class) //OK
 @ExtendWith({MockitoExtension::class, MainDispatcherExtension::class}) //NG

BeforeEachCallbackとBeforeTestExecusionCallbackの2種類がある

BeforeEachCallback#beforeEach

@BeforeEach

BeforeTestExecutionCallback#beforeTestExecution

@Test

の順番です。今回は@BeforeEachより前にMainを設定したいので、実装すべきExtensionはBeforeEachCallback、AfterEachCallbackです。

JUnit 5 User Guide
タイトルとURLをコピーしました