Unit Tests with Mockito and Jacoco in The Main Android Architectures — MVC, MVP, MVVM, Clean, MVI, VIPER, VIPER — part 3: MVVM

André Figas
4 min readOct 16, 2022

--

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

--

--