Andrew
Andrew

Reputation: 4712

Androidx datastore test: Ensure that you are only creating a single instance of datastore for this file

Currently, I am writing some test for my proto datastore. The only problem I have here is that I can't call a specific function because then my test fails / crashes. I find this very confusing, because all my other functions seem to work, except resetDatastore

Here is my code:

Repository

private companion object {
    private const val SHOP_FILTER_PRODUCT_DATASTORE: String = "shop_filter_product_datastore_test"
    private const val SHOP_FILTER_LIST_DATASTORE: String = "shop_filter_list_datastore_test"
    private const val SHOP_FILTER_BTN_DATASTORE: String = "shop_filter_btn_datastore_test"
}

private val testNonVolatileProductDataStore = context.createDataStore(
    fileName = SHOP_FILTER_PRODUCT_DATASTORE,
    serializer = ShopFilterProductSerializer
)

private val testNonVolatileListDataStore = context.createDataStore(
    fileName = SHOP_FILTER_LIST_DATASTORE,
    serializer = ShopFilterListSerializer
)

private val testNonVolatileBtnDataStore = context.createDataStore(
    fileName = SHOP_FILTER_BTN_DATASTORE,
    serializer = ShopFilterBtnSerializer
)

override suspend fun setValueProduct(newProduct: ShopFilterTempHolder) {
    if (newProduct.id == null || newProduct.mQuery == null) return
    testNonVolatileProductDataStore.updateData { preferences ->
        preferences.toBuilder().apply {
            positionId = newProduct.id!!
            query = newProduct.mQuery
        }.build()
    }
}

override suspend fun setValueList(newList: ShopFilterTempHolder) {
    if (newList.id == null || newList.mQuery == null) return
    testNonVolatileListDataStore.updateData { preferences ->
        preferences.toBuilder().apply {
            positionId = newList.id!!
            query = newList.mQuery
            mQueryDirection = newList.mQueryDirection
        }.build()
    }
}

override suspend fun setShopFilterBtn(value: Boolean) {
    testNonVolatileBtnDataStore.updateData { preferences ->
        preferences.toBuilder().apply {
            isChecked = value
        }.build()
    }
}

override suspend fun peekProductValue(): ShopFilterTempHolder {
    val temp = shopFilterProduct.first()
    return ShopFilterTempHolder(temp.positionId, temp.query)
}

override suspend fun peekListValue(): ShopFilterTempHolder {
    val temp = shopFilterList.first()
    return ShopFilterTempHolder(temp.positionId, temp.query, temp.mQueryDirection)
}

override suspend fun peekBtnValue(): Boolean = mappedShopFilterBtn.first()

override suspend fun resetDatastore() {
    testNonVolatileProductDataStore.updateData { preferences ->
        preferences.toBuilder().apply {
            positionId = Constants.SHOP_FILTER_DEFAULT_PRODUCT_ID
            query = Constants.SHOP_FILTER_DEFAULT_PRODUCT_QUERY
        }.build()
    }
    testNonVolatileListDataStore.updateData { preferences ->
        preferences.toBuilder().apply {
            positionId = Constants.SHOP_FILTER_DEFAULT_LIST_ID
            query = Constants.SHOP_FILTER_DEFAULT_LIST_QUERY
            mQueryDirection = Constants.SHOP_FILTER_DEFAULT_LIST_QUERY_DIRECTION
        }.build()
    }
    testNonVolatileBtnDataStore.updateData { preferences ->
        preferences.toBuilder().apply {
            isChecked = true
        }.build()
    }
}

Test

@Test
fun `values should be set to default`() = runBlocking {
    val newBtn =  false
    val newList =  ShopFilterTempHolder(0, "testString", 0)
    val newProduct =  ShopFilterTempHolder(0, "testString", 0)

    shopFilterValidator.tempBtnFilterValue = newBtn
    shopFilterValidator.tempListFilter = newList
    shopFilterValidator.tempProductFilter = newProduct

    shopFilterValidator.setNewBtnFilter()
    shopFilterValidator.setNewListFilter()
    shopFilterValidator.setNewProductFilter()

    assertEquals(newProduct, shopFilterDataStoreRepository.peekProductValue())
    assertEquals(newList, shopFilterDataStoreRepository.peekListValue())
    assertEquals(newBtn, shopFilterDataStoreRepository.peekBtnValue())

    shopFilterValidator.deleteAllValues()

    assertEquals(defautTempProductFilter, shopFilterDataStoreRepository.peekProductValue())
    assertEquals(defaultTempListFilter, shopFilterDataStoreRepository.peekListValue())
    assertEquals(defaultTempBtnFilterValue, shopFilterDataStoreRepository.peekBtnValue())
}

Stacktrace

Exception in thread "DefaultDispatcher-worker-2 @coroutine#5" java.io.IOException: Unable to rename C:\Users\Censored\AppData\Local\Temp\robolectric-Method_values_should_be_set_to_default1366629743868428403\com.example.app-dataDir\files\datastore\shop_filter_product_datastore_test.tmp.This likely means that there are multiple instances of DataStore for this file. Ensure that you are only creating a single instance of datastore for this file.
    at androidx.datastore.core.SingleProcessDataStore.writeData$datastore_core(SingleProcessDataStore.kt:303)
    at androidx.datastore.core.SingleProcessDataStore.transformAndWrite(SingleProcessDataStore.kt:280)
    at androidx.datastore.core.SingleProcessDataStore$actor$1.invokeSuspend(SingleProcessDataStore.kt:165)
    (Coroutine boundary)
    at kotlinx.coroutines.CompletableDeferredImpl.await(CompletableDeferred.kt:86)
    at androidx.datastore.core.SingleProcessDataStore$updateData$2.invokeSuspend(SingleProcessDataStore.kt:96)
    at androidx.datastore.core.SingleProcessDataStore.updateData(SingleProcessDataStore.kt:96)
    at com.example.app.repository.FakeDataStoreRepositoryImpl.deleteDataStore(FakeDataStoreRepositoryImpl.kt:86)
    at com.example.app.data.models.validator.ShopFilterValidator$deleteAllValues$1.invokeSuspend(ShopFilterValidator.kt:80)

Upvotes: 6

Views: 990

Answers (1)

Olga K
Olga K

Reputation: 31

not sure if that could help you, but in my case the problem occurred when running tests on Windows machine and wasn't there when switching to Linux or executing the test on the emulator instead

Upvotes: 3

Related Questions