androidkotlinunit-testingkotlin-coroutinesandroid-jetpack-datastore

Data Store clean up in unit test error - Only a single call to `runTest` can be performed during one test


Getting the following error,

Only a single call to runTest can be performed during one test.

In-unit tests for data store.

The issue is clearly because I have runTest in @After for data store cleanup after each test.

How do I fix this issue?

Data store testing reference

Migrating from runBlockingTest to runTest

Code

@HiltAndroidTest
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class MyPreferencesDataSourceTest {
    private val testContext: Context = ApplicationProvider.getApplicationContext()
    private val testDispatcher = UnconfinedTestDispatcher()
    private val testScope = TestScope(testDispatcher + Job())

    @get:Rule(order = 0)
    var hiltRule = HiltAndroidRule(this)

    @get:Rule(order = 1)
    val mainDispatcherRule = MainDispatcherRule()

    private val testDataStore: DataStore<Preferences> = PreferenceDataStoreFactory.create(
        scope = testScope,
        produceFile = {
            testContext.preferencesDataStoreFile(
                name = AppConstants.APP_NAME,
            )
        },
    )

    private lateinit var appPreferenceDataSource: AppPreferenceDataSource

    @Before
    fun setUp() {
        hiltRule.inject()
        appPreferenceDataSource = AppPreferenceDataSource(
            dataStore = testDataStore,
        )
    }

    @After
    fun tearDown() = testScope.runTest {
        testDataStore.edit {
            it.clear()
        }
        testScope.cancel()
    }

    @Test
    fun getCurrencyBase_returnsNull() = testScope.runTest {
        val result = appPreferenceDataSource.getData().first()

        Assert.assertNull(result)
    }
}

Solution

  • Though this is not exactly what I was looking for, this is the best solution I could find so far.

    I am open to better ways if there are any.

    Source - Kotlinlang Slack

    With a little modification from the Slack message, I created this helper function.

    private fun runTestAndCleanup(
        block: suspend () -> Unit,
    ) = testScope.runTest {
        block()
        testDataStore.edit {
            it.clear()
        }
    }
    

    And I replaced all the usages of testScope.runTest with runTestAndCleanup.