How to test Coroutines in Android App ?
Coroutines are used to handle long running and asynchronous code.
One incorrect way to solve this problem of asynchronous thread test is using this:
It's not correct becoz it's not tracking when these coroutines are actually finished or done.
It also doesn't ensure the order in which these coroutines will finish.
If we use thread too long, our test tends to be super slow.
So the best practice is to tell your test to wait and hope it waits long enough.
so instead in coroutines we use this : = runBlockingTest{
This = runBlockingTest{ will ensure that the test.blocks untils all of the coroutines it starts are finished. It also run the code in coroutines immediately and in the order in which they are called.
The way how this = runBlockingTest{ will do the job is by calling a TestCoroutineDispacher.
It uses this test dispacher to run and suspend functions in = runBlockingTest{
TestCoroutineDispatcher
They are the coroutine way of determining how a coroutine runs.
They control both things, like what thread the code should run on but also when and how the tasks
shoud run.
This dispatcher will run your code immediately and allows greater control over coroutine timing.
As long as your code and your test doesn't change what dispatcher that it's running on and it runs on a TestCoroutineDispatcher. The = runBlockingTest{ will resolve the vast majority of your coroutine testing issues.
kotlinx-coroutines-test
Both = runBlockingTest{ and TestCoroutineDispatcher are part of kotlinx-coroutines-test a currently experimental library for testing coroutines. When you need to annotate the function or class with @ Experimental coroutines API to avoid seeing a warning.
Use = runBlockingTest{
Not
runBlocking :
Finally, when you're writing code and test doubles, use runBlocking
# Exception in thread "main" java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used
override suspend fun saveTask(task: Task) { coroutineScope { launch { tasksRemoteDataSource.saveTask(task) } launch { tasksLocalDataSource.saveTask(task) } }}
@Testfun activeTaskDetails_DisplayedInUi() = runBlockingTest{ // GIVEN - Add active (incomplete) task to the DB val activeTask = Task("Active Task", "AndroidX Rocks", false) repository.saveTask(activeTask) // WHEN - Details fragment launched to display task val bundle = TaskDetailFragmentArgs(activeTask.id).toBundle() launchFragmentInContainer<TaskDetailFragment>(bundle, R.style.AppTheme) Thread.sleep(2000) // THEN - Task details are displayed on the screen // make sure that the title/description are both shown and correct onView(withId(task_detail_title_text)).check(matches(isDisplayed())) onView(withId(task_detail_title_text)).check(matches(withText("Active Task"))) onView(withId(R.id.task_detail_description_text)).check(matches(isDisplayed())) onView(withId(R.id.task_detail_description_text)).check(matches(withText("AndroidX Rocks"))) // and make sure the "active" checkbox is shown unchecked onView(withId(R.id.task_detail_complete_checkbox)).check(matches(isDisplayed())) onView(withId(R.id.task_detail_complete_checkbox)).check(matches(not(isChecked()))) }
One incorrect way to solve this problem of asynchronous thread test is using this:
Thread.sleep(2000)
It's not correct becoz it's not tracking when these coroutines are actually finished or done.
It also doesn't ensure the order in which these coroutines will finish.
If we use thread too long, our test tends to be super slow.
So the best practice is to tell your test to wait and hope it waits long enough.
so instead in coroutines we use this : = runBlockingTest{
This = runBlockingTest{ will ensure that the test.blocks untils all of the coroutines it starts are finished. It also run the code in coroutines immediately and in the order in which they are called.
The way how this = runBlockingTest{ will do the job is by calling a TestCoroutineDispacher.
It uses this test dispacher to run and suspend functions in = runBlockingTest{
TestCoroutineDispatcher
They are the coroutine way of determining how a coroutine runs.
They control both things, like what thread the code should run on but also when and how the tasks
shoud run.
This dispatcher will run your code immediately and allows greater control over coroutine timing.
As long as your code and your test doesn't change what dispatcher that it's running on and it runs on a TestCoroutineDispatcher. The = runBlockingTest{ will resolve the vast majority of your coroutine testing issues.
kotlinx-coroutines-test
Both = runBlockingTest{ and TestCoroutineDispatcher are part of kotlinx-coroutines-test a currently experimental library for testing coroutines. When you need to annotate the function or class with @ Experimental coroutines API to avoid seeing a warning.
Use = runBlockingTest{
Not
runBlocking :
- Used outside of tests
- Closer simulation to what the real code does
- Prefereable for fakes
Finally, when you're writing code and test doubles, use runBlocking
# Exception in thread "main" java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used
@ExperimentalCoroutinesApiclass TasksViewModelTest{ val testDispacher : TestCoroutineDispatcher = TestCoroutineDispatcher() @Before fun setupDispatcher(){ Dispatchers.setMain(testDispacher) } @After fun tearDownDispatcher(){ Dispatchers.resetMain() testDispacher.cleanupTestCoroutines() } ....
// write you test here ..
}
Comments
Post a Comment