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.Erik
06/07/2021, 7:12 AM@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)Erik
06/07/2021, 7:13 AMMarko Novakovic
06/07/2021, 10:19 AMrunBlocking so I tried to fix the error and I succeededMarko Novakovic
06/07/2021, 10:19 AMMarko Novakovic
06/07/2021, 10:20 AMFlow and MutableStateFlow? MutableStateFlow is not “regular” Flow and MutableStateFlow was causing issue in my testsMarko Novakovic
06/07/2021, 10:22 AMUninitialized 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 AMErik
06/07/2021, 10:49 AMfirst item is your initial stateErik
06/07/2021, 10:50 AMdrop(1), like you saidErik
06/07/2021, 10:50 AMMarko 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?Marko Novakovic
06/07/2021, 10:51 AMprivate val _state = MutableStateFlow(1)
val state: Flow<Int> = _stateErik
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 FlowErik
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 AMMarko 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 behaviourErik
06/07/2021, 10:54 AMMarko Novakovic
06/07/2021, 10:55 AMErik
06/07/2021, 10:55 AMErik
06/07/2021, 10:55 AMErik
06/07/2021, 10:55 AMMarko Novakovic
06/07/2021, 10:56 AMErik
06/07/2021, 10:56 AMErik
06/07/2021, 10:57 AMMarko Novakovic
06/07/2021, 10:59 AM