Marko Novakovic
06/04/2021, 6:06 PMThis 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.runBlockingTest(TestBuilders.kt:53)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest$default(TestBuilders.kt:45)
am trying to run some tests and I get this error? what is the cause of it and how to fix it?Erik
06/05/2021, 6:34 AMMarko Novakovic
06/07/2021, 4:05 AMrunBlockingTest
gives above error but runBlocking
does notErik
06/07/2021, 6:46 AMrunBlockingTest
is designed to fail when a (child) job isn't finished yet. runBlocking
doesn't care. Usually you shouldn't use runBlocking
in tests, because your tests might slow down a lot (delays will actually delay as long as they should) and leaking child coroutines won't be detected, leading to a fragile test system.@Test
fun test() = runBlocking {
println("Before launch")
launch(Dispatchers.Default /* Switch to non-blocking context */) {
delay(100)
println("Inside launch")
}
println("After launch")
}
This test will pass, but that's likely a bug! If you change it to runBlockingTest
, you will be properly notified that a job was still active when the test block was finished, which is a leaking child coroutine. In fact, the launch
call launches the child coroutine in a non-blocking context, so it can run concurrently with the blocking context. Also note the order of the println
output, which shows that the launched child job is running concurrently and still executes after the last println
statement (the bug)Marko Novakovic
06/07/2021, 10:19 AMrunBlocking
so I tried to fix the error and I succeededFlow
and MutableStateFlow
? MutableStateFlow
is not “regular” Flow
and MutableStateFlow
was causing issue in my testsUninitialized
state first and than state in response to function call. do I use drop(1)
or there is something else that I should use?Erik
06/07/2021, 10:48 AMMarko Novakovic
06/07/2021, 10:49 AMErik
06/07/2021, 10:49 AMfirst
item is your initial statedrop(1)
, like you saidMarko Novakovic
06/07/2021, 10:50 AMErik
06/07/2021, 10:50 AMMarko Novakovic
06/07/2021, 10:50 AMMutableStateFlow
directly and when I expose flow
backed by MutableStateFlow
?private val _state = MutableStateFlow(1)
val state: Flow<Int> = _state
Erik
06/07/2021, 10:51 AMFlow
or StateFlow
in the API, test the public versionMarko Novakovic
06/07/2021, 10:52 AMStateFlow
is hot flow and collect
doesn’t work as on standard Flow
Erik
06/07/2021, 10:53 AMprivate val _state = MutableStateFlow(1)
val state: Flow<Int> = _state.asStateFlow()
So that the consumer of state
cannot cast state as MutableStateFlow
(it could start emitting states if it could cast it!)Marko Novakovic
06/07/2021, 10:54 AMErik
06/07/2021, 10:54 AMFlow
interface specifies very generic collect
behaviour. The StateFlow
has more specific collect
behaviourMarko Novakovic
06/07/2021, 10:55 AMErik
06/07/2021, 10:55 AMMarko Novakovic
06/07/2021, 10:56 AMErik
06/07/2021, 10:56 AMMarko Novakovic
06/07/2021, 10:59 AM