Unit Tests with Mockito and Jacoco in The Main Android Architectures — MVC, MVP, MVVM, Clean, MVI, VIPER, VIPER — part 3: MVVM
MVVM
If you have some doubts about this architecture, check my other article.
Model
This layer is entirely testable, but It was already done when we tested an MVC project. I was following an incremental methodology. So, I won’t repeat myself. Here I will just refer to my previous article.
View
The view layer definitely is not the proposal for this kind of test. That assertion is valid for all architectures. I would recommend you again read about Espresso.
ViewModel
Here our main challenge is how effectively test LiveData. We could try to avoid this topic since an architecture layer must not depend on some library, so you could use another kind of observable class. But I am almost sure you probably have used it in your project, so let's support It.
Some problems that maybe you are having if you are stuck at this point.
Thread handling
What we have to do:
@Test
fun test(){
val data = MutableLiveData<String>()
data.value = "hello"
}
Problem:
java.lang.NullPointerException
at androidx.arch.core.executor.DefaultTaskExecutor.isMainThread
Solution:
build.gradle
testImplementation("android.arch.core:core-testing:1.1.1", {
exclude group: 'com.android.support', module: 'supp.ort-compat'
exclude group: 'com.android.support', module: 'support-annotations'
exclude group: 'com.android.support', module: 'support-core-utils'
})
gradle.properties
android.enableJetifier=true
Call this method before you change the MutableLiveData value
The next problem is:
How to get MutableLiveData mutations
If you prefer an official solution, you can check on android GitHub, I checked some limitations there. Basically, this utility is able only to get one mutation. I made some changes there to collect all mutations.
Now let’s go back to our project
Success Case
I won’t repeat myself talking about what I did at setup, and what these sections ‘given’, ‘when’, and ‘then’ means, So, if It sounds confusing to you, please, go back to the previous chapters.
Failure Cases
Our ViewModel data keeps null when our services fail. So, We have to validate this flow.
Cancel Cases
Our ViewModel did not have time to add anything to our result list.
Generating Test Results Report
./gradlew app:testMvvm_UnitTest
Output
app/build/reports/tests/testMvvm_UnitTest/index.html
Generating Coverage Report
./gradlew app:testMvvm_UnitTestCoverage
I had to create a new exclusion entry for our coverage report. I won’t test our ViewModelFactories
ext.excludes = [
'**/*ViewModelFactory.class',
'**/*Activity.class',
'**/Person.class',
'**/PersonDataModel.class',
'**/BuildConfig.*',
]
Output
app/build/reports/jacoco/testMvvm_UnitTestCoverage/index.html
You can check this project on GitHub. Remember to select the architecture that you wish to study on the build variants:
Unit Tests with Mockito and Jacoco in The Main Android Architectures — part 2: MVC
Unit Tests with Mockito and Jacoco in The Main Android Architectures — part 3: MVP
Unit Tests with Mockito and Jacoco in The Main Android Architectures — part 4: MVVM
Unit Tests with Mockito and Jacoco in The Main Android Architectures — part 5: VIPER
Unit Tests with Mockito and Jacoco in The Main Android Architectures — part 6: MVI
Unit Tests with Mockito and Jacoco in The Main Android Architectures — part 7: Clean