Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to test Proto datastore? The Medium article by Simona Stojanovic doesn't work for proto datastore #61

Open
sarimmehdi opened this issue Nov 24, 2022 · 0 comments
Assignees

Comments

@sarimmehdi
Copy link

sarimmehdi commented Nov 24, 2022

I tried the approach in Simona Stojanovic @simona-anomis Medium article but it doesn't work for proto datastore. The issue is that testCoroutineScope.runBlockingTest gives me an error.

The entire code is the same as in the Medium article except for the part where the datastore is created:

DataStoreFactory.create(
            serializer = UserPreferencesSerializer,
            produceFile = { testContext.dataStoreFile(DATA_STORE_FILE_NAME) },
            scope = testCoroutineScope
        )

This is the full test file I run as an Instrumented Test:

package com.xkcd.comiclist_data.repository

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.dataStoreFile
import androidx.test.platform.app.InstrumentationRegistry
import app.cash.turbine.test
import com.xkcd.core.XkcdSettings
import com.xkcd.core.settings.XkcdSettingsSerializer
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.test.*
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test

@HiltAndroidTest
class XkcdSettingsRepositoryImplTest {

    @get:Rule
    var hiltRule = HiltAndroidRule(this)

    private val testContext: Context =
        InstrumentationRegistry.getInstrumentation().targetContext
    private val testCoroutineDispatcher: TestCoroutineDispatcher =
        TestCoroutineDispatcher()
    @OptIn(ExperimentalCoroutinesApi::class)
    private val testCoroutineScope =
        TestCoroutineScope(testCoroutineDispatcher + Job())

    private val testDataStore: DataStore<XkcdSettings> = DataStoreFactory.create(
        serializer = XkcdSettingsSerializer,
        produceFile = { testContext.dataStoreFile(XkcdSettingsSerializer.testDataStoreName) },
        scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
    )

    private val repository: XkcdSettingsRepositoryImpl = XkcdSettingsRepositoryImpl(testDataStore)

    @OptIn(ExperimentalCoroutinesApi::class)
    @Before
    fun setup() {
        Dispatchers.setMain(testCoroutineDispatcher)
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    @After
    fun cleanUp() {
        Dispatchers.resetMain()
        testCoroutineDispatcher.cleanupTestCoroutines()
        testCoroutineScope.cancel()
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    @Test
    fun dummyTest() {
        testCoroutineScope.runBlockingTest {
            repository.getXkcdSettings().test {
                println(awaitItem())
                awaitComplete()
            }
        }
    }
}

The error I get states that This job has not completed yet:

java.lang.IllegalStateException: This job has not completed yet
at kotlinx.coroutines.JobSupport.getCompletionExceptionOrNull(JobSupport.kt:1190)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersDeprecatedKt.runBlockingTest(TestBuildersDeprecated.kt:67)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(Unknown Source:1)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersDeprecatedKt.runBlockingTest(TestBuildersDeprecated.kt:126)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(Unknown Source:1)
at com.xkcd.comiclist_data.repository.XkcdSettingsRepositoryImplTest.dummyTest(XkcdSettingsRepositoryImplTest.kt:60)

Here is the full XkcdSettingsRepositoryImpl:

package com.xkcd.comiclist_data.repository

import androidx.datastore.core.DataStore
import com.xkcd.comiclist_domain.model.Comic
import com.xkcd.comiclist_domain.model.CurrentXkcdSettings
import com.xkcd.comiclist_domain.repository.XkcdSettingsRepository
import com.xkcd.core.XkcdSettings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.flow
import javax.inject.Inject
import kotlin.math.max

class XkcdSettingsRepositoryImpl @Inject constructor(
    private val xkcdSettings: DataStore<XkcdSettings>
): XkcdSettingsRepository {

    override suspend fun updateUserName(userName: String) {
        xkcdSettings.updateData { settings ->
            settings.toBuilder().setUserName(userName).build()
        }
    }

    override suspend fun updateLatestComic(comic: Comic) {
        xkcdSettings.updateData { settings ->
            settings.toBuilder().setLatestComicNum(max(comic.num, settings.latestComicNum.toInt()).toLong()).build()
        }
    }

    override suspend fun updateSelectedTab(selectedTab: XkcdSettings.SelectedTab) {
        xkcdSettings.updateData { settings ->
            settings.toBuilder().setSelectedComicTab(selectedTab).build()
        }
    }

    override suspend fun updateSelectedComicSort(selectedComicSort: XkcdSettings.SelectedComicSort) {
        xkcdSettings.updateData { settings ->
            settings.toBuilder().setSelectedComicSort(selectedComicSort).build()
        }
    }

    override suspend fun getXkcdSettings(): Flow<CurrentXkcdSettings> = flow {
        xkcdSettings.data.collectLatest {
            emit(CurrentXkcdSettings(
                userName = it.userName,
                latestComicNum = it.latestComicNum.toInt(),
                selectedTab = it.selectedComicTab,
                selectedComicSort = it.selectedComicSort
            ))
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants