Reputation: 61
I updated these implementations in app/build.gradle from 1.0.0 to 1.1.1:
implementation "androidx.datastore:datastore-preferences:$androidXDatastore"
implementation "androidx.datastore:datastore-core:$androidXDatastore"
implementation "androidx.datastore:datastore-preferences-core:$androidXDatastore"
After that, some instrumentation tests failed, although the production code still works fine:
kotlinx.coroutines.test.UncompletedCoroutinesError: After waiting for 10s, the test coroutine is not completing, there were active child jobs: ["coroutine#1":StandaloneCoroutine{Active}@284ce4db]
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$2$1$2$1.invoke(TestBuilders.kt:349)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$2$1$2$1.invoke(TestBuilders.kt:333)
at kotlinx.coroutines.InvokeOnCancelling.invoke(JobSupport.kt:1431)
at kotlinx.coroutines.JobSupport.notifyCancelling(JobSupport.kt:1477)
at kotlinx.coroutines.JobSupport.tryMakeCancelling(JobSupport.kt:799)
at kotlinx.coroutines.JobSupport.makeCancelling(JobSupport.kt:759)
at kotlinx.coroutines.JobSupport.cancelImpl$kotlinx_coroutines_core(JobSupport.kt:675)
at kotlinx.coroutines.JobSupport.cancelCoroutine(JobSupport.kt:662)
at kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:159)
at kotlinx.coroutines.EventLoopImplBase$DelayedRunnableTask.run(EventLoop.common.kt:501)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280)
at kotlinx.coroutines.DefaultExecutor.run(DefaultExecutor.kt:109)
at [email protected]/java.lang.Thread.run(Thread.java:833)
Target class
class ScrollPositionDataStore(private val dataStore: DataStore<Preferences>) {
private val _displayedPositionOffsetKey = stringPreferencesKey("displayed_position_offset")
suspend fun saveScrollPositionStore(topRowIndex: Int, positionOffset: Int) {
val position = "$topRowIndex,$positionOffset"
dataStore.edit { preferences ->
preferences[_displayedPositionOffsetKey] = position
}
}
suspend fun getScrollPosition(): Pair<Int, Int>? = scrollPositionFlow.firstOrNull()
private val scrollPositionFlow: Flow<Pair<Int, Int>> = dataStore.data
.map { preferences ->
// On the first run of the app, we will use LinearLayoutManager by default
val i = preferences[_displayedPositionOffsetKey]
val split = i?.split(",")
if (split == null) Pair(4, 0)
else Pair(split[0].toInt(), split[1].toInt())
}
}
Test class
private const val POSITION_PREFERENCES_NAME = "position_preferences"
@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
class ScrollPositionDataStoreTest : JunitDataStoreTest(POSITION_PREFERENCES_NAME) {
private var inputDataStore: ScrollPositionDataStore = ScrollPositionDataStore(dataStore)
@Test
fun `ScrollPositionDataStore 入力した値をそのまま帰す`() {
scope.runTest {
dataStore.edit { it.clear() }
inputDataStore.saveScrollPositionStore(10, 15)
assertThat(inputDataStore.getScrollPosition())
.isEqualTo(Pair(10, 15))
}
}
@Test
fun `ScrollPositionDataStore 未入力時に400を返す`() {
scope.runTest {
dataStore.edit { it.clear() }
assertThat(inputDataStore.getScrollPosition())
.isEqualTo(Pair(4, 0))
}
}
@Test
fun `ScrollPositionDataStore マイナス値をそのまま還す`() {
scope.runTest {
dataStore.edit { it.clear() }
inputDataStore.saveScrollPositionStore(-1, -111)
assertThat(inputDataStore.getScrollPosition())
.isEqualTo(Pair(-1, -111))
}
}
}
@ExperimentalCoroutinesApi
abstract class JunitDataStoreTest(dataStoreName :String) {
private val dispatcher = UnconfinedTestDispatcher()
private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
val scope = TestScope(dispatcher + Job())
val dataStore = PreferenceDataStoreFactory.create(
scope = scope,
produceFile = { context.preferencesDataStoreFile(dataStoreName) }
)
@CallSuper
@Before
open fun setUp() {
Dispatchers.setMain(dispatcher)
}
@CallSuper
@After
open fun tearDown() {
Dispatchers.resetMain()
}
}
After reverting back to version 1.0.0 the tests work fine, so the problem must be caused by the new data store version.
How can I get my tests to pass again with the new data store version?
Upvotes: 4
Views: 328
Reputation: 15763
This is caused by a performance improvement introduced with version 1.1.0.
The recommended solution (see link above) is to provide a backgroundScope
to the DataStore initialization. It won't wait for coroutines to finish at the end of tests:
val dataStore = PreferenceDataStoreFactory.create(
scope = scope.backgroundScope,
produceFile = { context.preferencesDataStoreFile(dataStoreName) }
)
Upvotes: 5