Your view model class usually has several functionalities. It somehow fetches data, it optionally maps your data entities to displayable entities, handles clicks and performs action for given events. You want to be sure that after refactor your code will still work perfectly. To achieve that you need to write and maintain unit tests. I will show you simple way of creating unit tests for Android ViewModel implementation with step-by-step explanation and Test-Driven-Development spirit.

Prerequisites

In his example we will try to implement VenueDetailsViewModel in TDD style. TDD (Test Driven Development) is development technique, where developer relies on unit tests created before actual implementation.

Tools

I’ll be using Mockito and Mockito-Kotlin for mocking:

And Strikt for assertions:

Classes in package

Let’s consider screen which displays some venue details and allows user to mark that place as favorite. We have following constructions for our view model:

Repository fetches data from remote API and emits value or error and FavoriteVenueController can check if venue is already marked by user as favorite and change its favorite state in local storage.

Note that every component is described by an interface — at a moment we don’t care about real implementations.

Initial code

Our initial code is class extending androidx.lifecycle.ViewModel with some fields that are ready to be bound into layout. We also have start() method that will be invoked just after ViewModel is available to be used and favoriteClick() to handle click on favorite icon on screen. We also have errorCallback — function which will be implemented in the higher class (e.x Activity) to display error message in Snackbar or Toast.

As you can see, I wrote createViewModel() factory method with default values: mock<> from Kotlin-Mockito extensions. It will be useful while writing single test cases, to not invoke full constructor each time.

First test case

Let’s write our first test case. First of all, our ViewModel should display VenueDisplayable from repository. We will cover two basic cases — case when Venue is found and second when error is emitted.

Now it’s time to create assertion:

When you hit run test then you should have two tests failing, because no implementation is present yet. So lets write simple start method in our view model:

There are more ways to achieve that behavior — your unit tests should follow arrange-act-assert flow or given-when-then.

Arrange — create objects, implement every stuff that is given . Act — run your test method (in our case start() ) — when . Assert — check if desired change of state happened or verify if some mocked object was invoked with proper parameters — then .

More test cases!

Now our tests will pass. Let’s create more test cases, for setting venue favorite status:

If you run this test you will probably get NullPointerException on venueRepository.getOne(venueId) . That’s because we didn’t provided any stub for that method call. We already created in our tests default implementations for all class dependencies, but Mockito won’t handle default return values for us. We will add specific default implementation for all cases in our factory method. For stubbing rx calls I usually make use of .never() method:

Now our tests won’t throw NullPointerException . Instead there will be simple assertion fail. After that we can implement our functionality, and of course other test cases for checking if venue is favorite.

Of course we need to update our createViewModel factory method to return default value for venue controller too, so our previous test cases won’t throw NullPointerException .

Now we have one thing left — handling favoriteClick .

First, lets write tests in given-when-then manner:

Please note — in first test case I could’ve mock on { checkIsFavorite(FAKE_ID) } doReturn Single.just(false) , but it wouldn’t be unit test anymore since it would be bound with other components. That’s not desired behavior all the time. In that way, when something in the class breaks, you will have clear information which singular piece of code failed.

Now when we wrote our test cases, we can proceed with implementation:

Nothing too fancy — it passes test cases we wrote. Code prettifying can be done later.

Final code

We finally covered all major test cases, our tests are unit tests, they do not depend directly on implementation and are relatively fast. Let’s see how final code looks like:

And our implementation:

We can do much more in here. We should inject somehow proper Schedulers , we can indicate progress loading in some way, we can optimize repository calls and so on. We should also remember about disposing every call in onCleared method from ViewModel .

With this TDD-like process we were able to write tests that do not depend strongly on direct implementation. In that way, when you would someday change RxJava calls to Kotlin Coroutines, your base test cases won’t change.

Tips & tricks: