```This job has not completed yet java.lang.Illega...
# coroutines
m
Copy code
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.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?
e
You maybe are launching coroutines that are still running while your test block finishes. Those coroutines aren't completed yet, which is illegal, as your code is still running and potentially doing things that you haven't covered by the test.
m
No. Class am testing had only one function and am calling it once in tests.
runBlockingTest
gives above error but
runBlocking
does not
e
runBlockingTest
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.
For example:
Copy code
@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)
The solution in this example would be to inject the dispatcher, and in a test use the test dispatcher instead of a production dispatcher
m
@Erik thank you very much. I’ve been able to solve the issue. I didn’t want to use
runBlocking
so I tried to fix the error and I succeeded
one more question
how to test
Flow
and
MutableStateFlow
?
MutableStateFlow
is not “regular”
Flow
and
MutableStateFlow
was causing issue in my tests
my implementation emits
Uninitialized
state first and than state in response to function call. do I use
drop(1)
or there is something else that I should use?
e
I'm happy to answer your question, but next time ask a new one in the channel
m
I can ask this one in the channel
e
I would test 2 things:
- Assert that the
first
item is your initial state
- Assert the rest by
drop(1)
, like you said
Seem like a fine approach 👍
m
thank you very much
e
Good luck with your implementations! 🙂
m
does it make a difference if I test
MutableStateFlow
directly and when I expose
flow
backed by
MutableStateFlow
?
like this
Copy code
private val _state = MutableStateFlow(1)
val state: Flow<Int> = _state
e
Test the interface of your class, so if it has a public
Flow
or
StateFlow
in the API, test the public version
m
I understand. what I was asking is does it make a difference in tests.
StateFlow
is hot flow and
collect
doesn’t work as on standard
Flow
e
Consider using (just a detail)
Copy code
private 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!)
m
interesting. thanks
will do that
e
The
Flow
interface specifies very generic
collect
behaviour. The
StateFlow
has more specific
collect
behaviour
Depending on what behaviour you want to expose, you expose a flow or state flow
m
that’s what I was asking. thank you for your time, this was really helpful
e
E.g. a flow doesn't promise that there is a value, but a state flow does promise that there always is one and that it is always repeated
👍 1
You're welcome
Feel free to ask anything on the #coroutines channel, there's enough helpful folk around
m
I will, and I will post new questions in new messages 😄
e
Depending on how complex you want your tests to become, try this library to test flows: https://github.com/cashapp/turbine
You might not need it, but take a look at what it can do, maybe you discover that it helps you test your flows!
m
am taking a look now
195 Views