0

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)
    }
}
Abhimanyu
  • 11,351
  • 7
  • 51
  • 121

2 Answers2

0

Great question what you’re supposed to do if you have async stuff to run after or afterEach.

For this case you can try:

@After
fun teardown() {
    File(context.filesDir, "datastore").deleteRecursively()
}

Other wise there are more ideas here: There are multiple DataStores active for the same file in HiltAndroidTest

ryankuck
  • 320
  • 3
  • 15
0

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.

Abhimanyu
  • 11,351
  • 7
  • 51
  • 121