原因
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