Unit testing coroutines runBlockingTest: This job has not completed yet Unit testing coroutines runBlockingTest: This job has not completed yet android android

Unit testing coroutines runBlockingTest: This job has not completed yet


As can be seen in this post:

This exception usually means that some coroutines from your tests were scheduled outside the test scope (more specifically the test dispatcher).

Instead of performing this:

private val networkContext: CoroutineContext = TestCoroutineDispatcher()private val sut = Foo(  networkContext,  someInteractor)fun `some test`() = runBlockingTest() {  // given  ...  // when  sut.foo()  // then  ...}

Create a test scope passing test dispatcher:

private val testDispatcher = TestCoroutineDispatcher()private val testScope = TestCoroutineScope(testDispatcher)private val networkContext: CoroutineContext = testDispatcherprivate val sut = Foo(  networkContext,  someInteractor)

Then in test perform testScope.runBlockingTest

fun `some test`() = testScope.runBlockingTest {  ...}

See also Craig Russell's "Unit Testing Coroutine Suspend Functions using TestCoroutineDispatcher"


This is not an official solution, so use it at your own risk.

This is similar to what @azizbekian posted, but instead of calling runBlocking, you call launch.As this is using TestCoroutineDispatcher, any tasks scheduled to be run without delay are immediately executed. This might not be suitable if you have several tasks running asynchronously.

It might not be suitable for every case but I hope that it helps for simple cases.

You can also follow up on this issue here:

If you know how to solve this using the already existing runBlockingTest and runBlocking, please be so kind and share with the community.

class MyTest {    private val dispatcher = TestCoroutineDispatcher()    private val testScope = TestCoroutineScope(dispatcher)    @Test    fun myTest {       val apiService = mockk<ApiService>()       val repository = MyRepository(apiService)              testScope.launch {            repository.someSuspendedFunction()       }              verify { apiService.expectedFunctionToBeCalled() }    }}


According to my understanding, this exception occurs when you are using a different dispatcher in your code inside the runBlockingTest { } block with the one that started runBlockingTest { }.

So in order to avoid this, you first have to make sure you inject Dispatchers in your code, instead of hardcoding it throughout your app. If you haven't done it, there's nowhere to begin because you cannot assign a test dispatcher to your test codes.

Then, in your BaseUnitTest, you should have something like this:

@get:Ruleval coroutineRule = CoroutineTestRule()
@ExperimentalCoroutinesApiclass CoroutineTestRule(    val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()) : TestWatcher() {    override fun finished(description: Description?) {        super.finished(description)        Dispatchers.setMain(testDispatcher)    }    override fun starting(description: Description?) {        super.starting(description)        Dispatchers.resetMain()        testDispatcher.cleanupTestCoroutines()    }}

Next step really depends on how you do Depedency Injection. The main point is to make sure your test codes are using coroutineRule.testDispatcher after the injection.

Finally, call runBlockingTest { } from this testDispatcher:

@Testfun `This should pass`() = coroutineRule.testDispatcher.runBlockingTest {    //Your test code where dispatcher is injected}