Sign in Go Pro

Android Model-View-ViewModel Pattern

Testing the ViewModel

This lesson is for PRO members.

Upgrade today to get access to all the PRO lessons.

Unlock this lesson

Up next



Before the ViewModel is used by the View, it needs to be tested. It's important to test the ViewModel in isolation so that changes to it's dependencies do not have unintended side effects on your ViewModel tests. The best way to do this, is by mocking your ViewModels dependencies and testing for interactions with those dependencies.

In this lesson you will learn how to:
* Unit test your ViewModels in Isolation and understand the "why".
* Integrate Mockito, a mocking framework into your application.
* Manage dependency versions with variables defined in your project build.gradle.
* Use mocks & stubs to control and validate interactions with your ViewModels dependencies.
* Mock final classes and functions, using an opt-in feature of Mockito called mock-maker-inline.


you can use Mockito in line and you will not need the extra mock-maker-inline file.
testImplementation “org.mockito:mockito-inline:2.8.47”, this extra file generate conficts whit Java and Kotlin code, when I was using the file, with multiple java and kotlin modules , I get some conflicts, the solution was, used mockito inline

Hi Andreas, thanks for sharing! The mockito-inline dependency is a convenience mechanism that does the same thing under the hood as what we do in this lesson.

It's interesting that this wouldn't work when doing this manually, but I haven't tried it in a project where I've added mixed Java and Kotlin modules. If this is working better, I'd say go for it! :-) .

It would be interesting to see if we could solve it without the added dependency. If you have a sample project that produces the conflict/error, I'd be happy to take a look!

@Eric, why do you use anyDouble() and anyInt() as bad input parameters? Is it because Mockito uses null as default for these? Can anyDouble be equal to 15.0?

Hi Angelina,

For these tests testCalculateTipBadTipPercent and testCalculateTipBadCheckInputAmount, the bad input parameters are the empty strings set on inputTipPercentage and inputCheckAmount respectively.

For example:
fun testCalculateTipBadTipPercent() {
calculatorViewModel.inputCheckAmount = "10.00"
calculatorViewModel.inputTipPercentage = "" // bad input

fun testCalculateTipBadCheckInputAmount() {
calculatorViewModel.inputCheckAmount = "" // bad input
calculatorViewModel.inputTipPercentage = "15"

In both of these tests, calling calculate tip on the viewModel (e.g. calculatorViewModel.calculateTip()) should not result in calculate tip being called on the model (e.g. updateOutputs(calculator.calculateTip(checkAmount, tipPct))). In the test we verify this with the line:

verify(mockCalculator, never()).calculateTip(anyDouble(),anyInt())

Here, we are asking Mockito to verify that the mock calculator never receives calculateTip function call. We use anyDouble and anyInt to wildcard the inputs. We don't care what those numbers are for this test, we just want to ensure that this function is not called for any combination of check amount and tip percentage. We could use real numbers here, such as 15.0 and some random int, but anyDouble and anyInt document our intent better and is a bit safer too. It is safer because our intent is to ensure calculateTip is never called, if we pass real numbers (such as verify(mockCalculator, never()).calculateTip(15.0, 10)), then the test is only verifying that calculateTip is not getting called with that precise number combination.

@Eric I have a question related to this snippet:



When you are using the first line you are testing the value that comes from mockCalculator.calculateTip with the stub that you've provided?

if yes, what it's the reason to check the stub with calculatorViewModel in the second line?

Hi Yuri,

The when(mockCalculator.calculateTip(10.00,15)).thenReturn(stub) line doesn't actually assert or verify anything. It just instructs the mock Calculator model to return the stub value when it's calculateTip function is called with the numbers 10.00 and 15. Remember that this function is not called from within the test, however we expect it to be called in the ViewModel with those exact amounts and the results to be set in the ViewModel's tipCalculation property.

The assertEquals(stub,calculatorViewModel.tipCalculation) call verifies our expectation.

Hello Eric.

What am I missing here? This test is not working:


Lessons in Android Model-View-ViewModel Pattern