Issue
Running Datastore instrumented test fails with kotlinx.coroutines.test.UncompletedCoroutinesError: After waiting for 60000 ms, the test coroutine is not completing, there were active child jobs: [ScopeCoroutine{Active}@e24f0d2]
So far Tried to:
- Use runBlockingTest or runBlocking instead of runTest
- Tried to call runBlocking/runBlockingTest and runTest by the testDispatcher
- Changed the context of runTest
- Launched the collection of flow in test and consumed it before finally cancelling the job
- Tried to use Flow Terminal operators (e.g. first(), take()) to test flow instead of turbine test
TestAppModule
@Module
@TestInstallIn(components = [SingletonComponent::class], replaces = [LocalModule::class])
object TestAppModule {
//...
@OptIn(ExperimentalCoroutinesApi::class)
@Singleton
@Provides
fun provideFakePreferences(
@ApplicationContext context: Context,
): DataStore<Preferences> {
return PreferenceDataStoreFactory
.create(
scope = TestScope(),
produceFile = {
context.preferencesDataStoreFile("test_pref_file")
}
)
}
}
RepoPreferencesTest
@OptIn(ExperimentalCoroutinesApi::class)
@HiltAndroidTest
@RunWith(AndroidJUnit4::class)
class RepoPreferencesTest {
@get:Rule
val hiltRule = HiltAndroidRule(this)
@Inject
lateinit var testDataStore: DataStore<Preferences>
lateinit var repoPreferences: RepoPreferences
private val testCoroutineDispatcher = UnconfinedTestDispatcher()
private val testCoroutineScope = TestScope(testCoroutineDispatcher + Job())
@Before
fun setup() {
hiltRule.inject()
repoPreferences = RepoPreferencesImpl(testDataStore)
}
@Test
fun getInitialLanguageFromInitially_ReturnsDefaultValue() = runTest {
repoPreferences.language.test {
assertThat(awaitItem()).isEqualTo(RemoteConstants.DEFAULT_LANGUAGE)
cancelAndIgnoreRemainingEvents()
}
}
//...
}
Solution
The problem is probably with the EmptyCoroutineContext that TestScope uses by default.
I have overridden the context of TestScope with UnconfinedTestDispatcher (which launches coroutines eagerly) and a Job and passed that scope to the PreferenceDataStoreFactory instead:
CoroutineTestModule
@OptIn(ExperimentalCoroutinesApi::class)
@Module
@TestInstallIn(components = [SingletonComponent::class], replaces = [CoroutineModule::class])
object CoroutineTestModule {
@Provides
@Singleton
fun provideTestCoroutineScope(): CoroutineScope {
val testCoroutineDispatcher = UnconfinedTestDispatcher()
return TestScope(testCoroutineDispatcher + Job())
}
}
TestAppModule
@Module
@TestInstallIn(components = [SingletonComponent::class], replaces = [LocalModule::class])
object TestAppModule {
//...
@Singleton
@Provides
fun provideFakePreferences(
@ApplicationContext context: Context,
scope: CoroutineScope
): DataStore<Preferences> {
return PreferenceDataStoreFactory
.create(
scope = scope,
produceFile = {
context.preferencesDataStoreFile("test_pref_file")
}
)
}
}
Answered By - Astro
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.