MVVM with Coroutine Flow and Unit Tests in Kotlin — Part 2
1.Before you begin:
This blog is continuation of the previous blog .In this blog we will learn how to write unit tests for coroutines with flows using MVVM architecture in Kotlin.
The link to the part 1 is given below:
Prerequisites:
- Familiarity with the Architecture Components
ViewModel
,LiveData
,Repository
. - Experience with Kotlin syntax, including lambdas.
- Familiarity with Mockito and JUnit testing Framework
What you’ll do ?
Use Mockito testing framework to unit test ViewModel in developed app .
Getting Setup :
The code is available in the above Github repository.
git clone https://github.com/chandragithub2014/MMVMCoroutineFlows.git
Unit Testing Concepts:
InstantTaskExecutorRule:
A JUnit Test Rule that swaps the background executor used by the Architecture Components with a different one which executes each task synchronously.
You can use this rule for your host side tests that use Architecture Components which means when writing unit tests for view model this rule instantly returns the asynchronous task like connecting with Network and so on. It is needed to test code with LiveData.
TestCoroutineRule:
It sets the main dispatcher to testCoroutineDispatcher
, runs the test then resets and cleanup. It also creates testCoroutineScope
in which we can run our tests.
@get:Rule
val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule()
@get:Rule
val testCoroutineRule = TestCoroutineRule()
Mocking :
We mock the required classes of ViewModel using @mock annotation.
@Mock
private lateinit var apiHelper: EmployeeService
@Mock
private lateinit var employeeRepository: EmployeeRepository
Unit Test a method in ViewModel:
private val testDispatcher = TestCoroutineDispatcher()
@Test
fun test_fetchEmployeeList() = testDispatcher.runBlockingTest {
var data = UserModel.Data(
"https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg",
"george.bluth@reqres.in",
"George",
1,
"Bluth"
)
var ad = UserModel.Support("StatusCode Weekly","http://statuscode.org/",)
var list = arrayListOf<UserModel.Data>()
list.add(data)
var retroResponse = UserModel(list,2,6,ad,12,12)
val employeeViewModel = EmployeeViewModel(testDispatcher,employeeRepository)
val response = Response.success(retroResponse)
val channel = Channel<Response<UserModel>>()
val flow = channel.consumeAsFlow()
Mockito.`when`(employeeRepository.fetchEmployees()).thenReturn(flow )
launch {
channel.send(response)
}
employeeViewModel.fetchEmployeeList()
Assert.assertEquals(1,employeeViewModel.employeeLiveData.value?.data?.size)
Assert.assertEquals(false, employeeViewModel.fetchLoadStatus()?.value)
}
In the above created a Dummy model class and created a flow out of it which repository returns after it communicates with Network.
Since we mocked the repository class, we will return the created flow with below code sample:
Mockito.`when`(employeeRepository.fetchEmployees()).thenReturn(flow )
We initialize viewmodel with mocked Repository class.
val employeeViewModel = EmployeeViewModel(testDispatcher,employeeRepository)
We verify the value of the mocked response when view model method is called
employeeViewModel.fetchEmployeeList()
Assert.assertEquals(1,employeeViewModel.employeeLiveData.value?.data?.size)
Congratulations:
In these two blogs, we have covered Coroutines with Flows in MVVM Architecture with unit tests with a simple app.