Unit Testing LiveData With Mockito and Truth

Eric Diaz
The Startup
Published in
5 min readSep 25, 2019

--

I completed a project that used LiveData to emit data to my views from my ViewModel. In this article, I will review the tests I wrote for my ViewModel in hopes that this information will be useful to someone out there in the Android World.

My ViewModel

This is the entire ExhangeRateViewModel class and I’ll walk through some of its parts to explain the logic.

I used Dagger to inject my BaseRepository implementation into the ExchangeRateViewModel. This is important because, in testing, I can mock an instance of my repository and have control over the returned object from my requestExchangeRates() method, which will be covered in the following code snippet.

The getRates() method initiates the subscription to the repository method requestExchangeRates(), which then initiates the network call to the API. As previously stated, we’ll be mocking the networking behavior in the tests. The exchangeRateData object is an instance of MutableLiveData<State>, hence why we call setValue() and pass in a State object.

Note, you normally only call setValue() on the MainThread, otherwise, this will throw an exception. For this example, I have omitted observing on the MainThread(in the Rx chain) because Unit tests are stateless. To observe the eventual LiveData(the getExchangeRateData() method returns the MutableLiveData as its superclass, LiveData, for observation), you also need to be on the MainThread, but I will review how to configure your tests to work off the MainThread in the tests section. Here’s the breakdown,

  1. Make the call to the repository method to contact the API
  2. Delay the subscription with a Completeable Action of first emitting a Loading State. This will tell the UI to show a progress dialog bar until a response is returned from the network. We will expect this to occur in our tests as well.
  3. Finally, subscribe to the source. Since this a Single, it will either onSuccess or onError. Here is where we get a value set for our LiveData and this is what we will be testing.

The Tests

This is the test class and I will be going over 2 test scenarios. But first, the setUp.

Setting Up Tests

First, The proper Gradle dependencies are needed to access the necessary testing components.

Now let’s review the ViewModelTest class members.

ViewModelTest Class Members

  1. The first member is the executorRule and this comes from the androidx.arch.core:core-testing library. Declaring an instance of this class and annotating with @Rule, will switch the default executor that LiveData uses to one that allows LiveData to be observed off the MainThread. The Documentation states — A JUnit Test Rule that swaps the background executor used by the Architecture Components with a different one which executes each task synchronously. You can use this rule for your host side tests that use Architecture Components.”
  2. The rest are more obvious, the mockRepository will be a mocked instance of the repository which the testSubject(the ExchangeRateViewModel) needs as a dependency, and lastly the mockObserver, which will serve as our stateless observer for the LiveData emissions.

The setUp Method

  1. First, create a mock of the repository using Mockito’s mock() method and pass in the class type into the method’s parameter.
  2. Then initialize an instance of the ViewModel and pass in the required dependency.
  3. Create a mock of the observer following the same steps in Step 1.
  4. Attach the observer to the LiveData located in the ViewModel by using the get method and accessing the observeForever() method of LiveData, passing in our observer. The observeForever() method defaults all observation of LiveData to the given observer.

Test 1

In summary, this test asserts that if the network returns a successful response from the API and data is received, then the ViewModel should emit a Success State with that data. Now we’ll review by the Given/When/Then breakdown.

Given — this dummy data(date, baseCurrency), The expected Response should be an empty model instance wrapped in a Single, to match the return type of repository method.

When- the mockRepository — that has been passed into our testSubject ViewModel is invoked; and its requestExchangeRates method is called, then return the expectedResponse.

It is important to understand that we can mock this response because Mockito allows us to dictate the behaviors of mocked objects. When the testSubject calls getRates(), it will invoke the mock object to return something. Since its not a “real” repository, we must instruct it on what to return. No actual computation or networking is done when using a mock.

Then — the results of the when action should result in a positive response, let’s review each expectation.

This is a basic Assertion test that gets the LiveData value and asserts that the result from the getValue method isEqualTo a Success State object with the expected empty model instance.

Remember that in our Rx chain we block the subscription with a Loading State and then wait for the network response. Now we verify that our mockObserver’s onChange() method is called and should emit a Loading State. Following that, since we are expecting a Success State, we verify that behavior as well. Note — This is not testing the order of the emission though they are written in order. This is only verifying that each event is occurring, in no particular order.

Next, verify that these emissions only occur once each. If we had any more, then we would have some unexpected behavior that needs to be addressed.

Finally, verify that we never receive a Failure State emission. This is important because we have constructed our test to emit a successful response. If this test fails, there may be some logic in between the repository and ViewModel that may need fixing. Lastly, we are verifying that no other emission or events are occurring with the mock.

Test 2

Test 2 is much like the first except that we are expecting an error to occur, and therefore a Failure State should be emitted. I won't go through all the code as much of it is the same, but I’ll highlight the difference from the first test.

Here’s the Given/When/Then breakdown.

The difference here is that since we want our repository to return an error, so we prep an exception and wrap it in a Single.

Pretty much the same as before only now we are instructing the mock to return the expectedError.

This time we are verifying that onChange() is called once with a Failure State as opposed to a Sucess, and we never get a Success emission from this behavior.

I hope this article has been helpful. Good luck!

You can find the GitHub Repository here — https://github.com/ericdiazjr21/ExchangeMe

You can follow me on LinkedIn here — https://www.linkedin.com/in/eric-diaz-32054bb8/

--

--