Testing ViewModels without losing the will to live
A ViewModel “sits” quite close to the activity/fragment. Something that a user would be looking at generally, even if they are unaware.
You would think that a professional keyboard user walking into your ViewModel test file would probably be able to read the test methods and make some sense of what’s going on at the screen level without even running your app, right? 🤡
Get some dependencies first
Where we left off
Our little ViewModel looks like this:
Most android apps out there are based on this simple premise.
The PostViewModel is responsible for fetching a post from Reddit while the calling activity/fragment is responsible for observing the stateData variable.
Googling “LiveData testing how to” (and its myriad of variations), you’ll find all sorts of smart extension functions (LiveDataTestUtil and others like it) that hide what’s going on and are not really that useful on a number of occasions.
A simple solution is to use a LifeCycleTestOwner helper class. (at the cost of adding a few extra lines of code that is)
We’ll use this to replicate the presence of an activity or fragment in our test class.
InstantTaskExecutorRule comes bundled in the androidx.arch.core:core-testing library and should be used when testing LiveData.
The stateObserver variable is just a mock. Think of it as the observer in the activity/fragment.
The lifeCycleTestOwner plays the role of the lifecycle of the activity/fragment and is created and destroyed before each test.
Unit tests do have their place, although they can get a bit overboard. A rather sad test on this occasion really goes for isolation and tests every little thing that can happen separately.
While this test will pass and it does verify a certain behavior, we can do better and provide some sort of documentation and high level view of the ViewModel to a stranger who doesn’t really know what going on in that file.
Unit tests will never replace decent UI tests on android (or your userbase throwing 1-star reviews on your app because it’s crashing all over the place), but this is good enough to build on for more complex screens. Much of the verbosity can also be decreased by using extension functions and test rules.