https://kotlinlang.org logo
#coroutines
Title
# coroutines
j

jean

01/17/2023, 11:26 AM
Can someone explain to me why it works with
UnconfinedTestDispatcher
and not with
StandardTestDispatcher
?
Copy code
private val testCoroutineDispatcher = UnconfinedTestDispatcher()
private val testCoroutineScope = TestScope(testCoroutineDispatcher)
private val testContext = ApplicationProvider.getApplicationContext<Context>()

private val sessionRepository: SessionRepository =
    PersistentSessionRepository(
        DataStoreFactory.create(
            serializer = SessionPreferencesSerializer,
            scope = testCoroutineScope,
            produceFile = { testContext.preferencesDataStoreFile("test-session-preferences") }
        ),
        routeRepository = routeRepository,
    )

@Test
fun sessionShouldInitiallyBeNull() = runTest(testCoroutineDispatcher, 5000) {
    assertNull(sessionRepository.session())
}
When I use
StandardTestDispatcher
I get this error :
After waiting for 60000 ms, the test coroutine is not completing
kotlinx.coroutines.test.UncompletedCoroutinesError: After waiting for 60000 ms, the test coroutine is not completing
d

Dmitry Khalanskiy [JB]

01/17/2023, 11:31 AM
Not sure if it's related to your problem (probably it is), but it's incorrect to create a
TestScope
and not call
runTest
on it.
runTest(testCoroutineDispatcher, 5000)
testCoroutineScope.runTest
j

jean

01/17/2023, 11:44 AM
if I use
testCoroutineScope.runTest
then I get this :
Channel was closed
kotlinx.coroutines.channels.ClosedSendChannelException: Channel was closed
d

Dmitry Khalanskiy [JB]

01/17/2023, 11:45 AM
Well, there you have it. The test fails with an exception for some reason, and now you see the reason. Before using
testCoroutineScope
in
runTest
that exception was just swallowed.
j

jean

01/17/2023, 11:47 AM
Copy code
override suspend fun session() = dataStore.data
    .firstOrNull()
    ?.let { wrapperToSession(it) }
What would cause a channel to close with this? I’m just trying to get the current value of a
DataStore
on which I don’t have control
d

Dmitry Khalanskiy [JB]

01/17/2023, 11:51 AM
I don't have any idea about what
DataStore
is, maybe someone else can answer this. From what I see, the test code itself doesn't fail (or not using
testCoroutineScope
in
runTest
wouldn't matter). Rather, something else that uses this scope fails with the exception. I suggest looking more closely at the stacktrace to find the specific code that fails.
s

Sam

01/17/2023, 11:54 AM
Perhaps passing
scope = testCoroutineScope.backgroundScope
to the
DataStoreFactory
would help?
d

Dmitry Khalanskiy [JB]

01/17/2023, 11:55 AM
Unlikely.
backgroundScope
helps with the test hanging due to background work being executed, but this is the case of the producer failing for some reason, so the consumer times out when waiting.
j

jean

01/17/2023, 11:55 AM
DataStore is a part of android jetpack. But as you suggested I ran the test with
stacktrace
and turned out I forgot to also use
testCoroutineScope.runTest
on a
@After
test function that clears my store. After updating it, I have another issue :
Only a single call to runTest can be performed during one test
d

Dmitry Khalanskiy [JB]

01/17/2023, 11:58 AM
Yep, what it says. Try using just
runBlocking
in
@After
. Or maybe it's possible not to use coroutines in
@After
at all.
j

jean

01/17/2023, 11:58 AM
Yes so if I change my code to this it works, thanks for the help!
Copy code
@Test
fun sessionShouldInitiallyBeNull() = testCoroutineScope.runTest {
    assertNull(journeySessionRepository.journeySession())
    journeySessionRepository.clearSession()
}
168 Views