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

André Figas
4 min readOct 21, 2022

--

MVI

If you have some doubts about this architecture, check my other article.

I would not say here we have one more layer. Basically here we a particular way to send signals from the view layer to the connector layer. That connector could be a ViewModel, Presenter or other kind of connector that you know.
About that particular way, we call here Intent, there could be splited in 3: State, Effect, Event. That was my review to avoid you freak out when I say something like MVI+Presenter, MVI+ViewModel

Since this classes won’t have logic, It will be excluded by our code coverage.

MVI+Presenter

Success

This method anyState is only to make our code pass through the kotlin null checking. But remember our view is mocked, so It won’t really consider that it receive as parameter. So, use a Loading status like as a mock status, won’t will affect our results.

Failure

Cancel

Generating Test Results Report

./gradlew app:testMvi_with_presenter_UnitTest

Output

app/build/reports/tests/testMvi_with_presenter_UnitTest/index.html

Generating Coverage Report

./gradlew app:testMvi_with_presenter_UnitTestCoverage

Output

app/build/reports/jacoco/testMvi_with_presenter_UnitTestCoverage/index.html

Here We coutgh that I considered the first false positive in our report. I’d like to share it with you in advance to help you if you face a similar situation in your project. I will advocate here why I considered it a false positive. But If you disagree, please use the comments sections.

Branches (C1 Coverage)

JaCoCo also calculates branch coverage for all if and switch statements. This metric counts the total number of such branches in a method and determines the number of executed or missed branches. Branch coverage is always available, even in absence of debug information in the class files. Note that exception handling is not considered as branches in the context of this counter definition.

If the class files haven been compiled with debug information decision points can be mapped to source lines and highlighted accordingly:

No coverage: No branches in the line has been executed (red diamond)

Partial coverage: Only a part of the branches in the line have been executed (yellow diamond)

Full coverage: All branches in the line have been executed (green diamond)

Since PersonEvent is sealed class, I know all possibilities, I assure there are only this 3 subclasses, so, we are already covering all possibilities. Just for despist, I will replace the last case for a default case just for testing.

Sorry Jacoco, I will keep with my original approach. 😅

MVI + ViewModel

For testing livedate I have to resort some utilities. I you did not read the article where I wrote about MVVM, I recommend you read it. Because I will reuse that utilities here.

Success

Failure

Here we had to validate a LiveData and a PublishSubject. So, we basically assure we only receive a Loading as a PageState and a Failure as Effect.

Cancel

Generating Test Results Report

./gradlew app:testMvi_with_viewmodel_UnitTest

Output

app/build/reports/tests/testMvi_with_viewmodel_UnitTest/index.html

Generating Coverage Report

./gradlew app:testMvi_with_viewmodel_UnitTestCoverage

Output

app/build/reports/jacoco/testMvi_with_viewmodel_UnitTestCoverage/index.html

Based on my article about VIPER, you could deduce would be completely viable to have MVI + VIPER, and you would be right. You will get the same result based in this adaption from MVP to MVI+Presenter, and my previous article about Viper. I implemented It, and you can check it on the github project. I won’t put it here to avoid this article become excessively long.

--

--