Effective LiveData Testing
In this article, I am going to explain how to perform live data testing effectively. In my previous articles on unit testing, I have mentioned about observe live data directly instead of creating view states and in another one view states and observing those view states
Please go through the below mention article if you want otherwise this article is independent of previous articles.
In my previous article, I haven’t tested sequence of operation and their correct value type, like for an API call firstly we show loading operation then we hide loading when we receive data then we show data or error based on network response.
Set Up::
To test this I am going to use https://github.com/jraska/livedata-testing
jraska library.
The article can be found on below link
https://android.jlelse.eu/effective-livedata-and-viewmodel-testing-17f25069fcd4
Add below dependency in build.gradle file
Kotlin users:
testImplementation 'com.jraska.livedata:testing-ktx:1.1.0'Java users:
testImplementation 'com.jraska.livedata:testing:1.1.0'
If you are not using androidx
namespace yet, please use version 0.2.1
Don’t forget to use
InstantTaskExecutorRule
fromandroidx.arch.core:core-testing
to make your LiveData test run properly.
TestObserver to easily test LiveData and make assertions on them. Using this we can buffer emitted data.
Let's get into the code, how to use this library and how to write livedata testing using this.
View Model
1. viewModel.getRecommendationData() will return Livedata<List<Product>>
2. viewModel.getIsLoadingData() will return Livedata<Boolean>
3. viewModel.getErrorData() will return LiveData<Throwable>
import android.arch.core.executor.testing.InstantTaskExecutorRule;
import com.jraska.livedata.TestObserver;public class ProductViewModelTest {@Rule public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();@Rule public RxImmediateSchedulerRule timeoutRule = new RxImmediateSchedulerRule();@Mock Api api;
private ProductViewModel viewModel;
private TestObserver<Boolean> loadingObserver;
private List<Boolean> loadingStates;@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);// Init your view model
viewModel = new ProductViewModel(api, null, null, null);
}@Test
public void testProduct_whenReturnsData() {//Assemble
List<Product> productRecommendation = new ArrayList<>();when(api.fetchProductRecommendation(null)).thenReturn(Single.just(productRecommendation));TestObserver<List<Product>> dataObserver= TestObserver.test(viewModel.getRecommendationData());loadingObserver = TestObserver.test(viewModel.getIsLoadingData());
loadingStates = loadingObserver.valueHistory();//Act
viewModel.fetchProductRecommendation(null);//Verify
loadingObserver.assertHistorySize(2);
assertEquals(true, loadingStates.get(0));
assertEquals(false, loadingStates.get(1));
assertEquals(productRecommendation, dataObserver.value());
}@Test
public void testProduct_whenThrowsError() {//Assemble
Throwable throwable = new Throwable(“Exception”);
when(hibbettApi.fetchProductRecommendation(null)).thenReturn(Single.error(throwable));TestObserver<Throwable> errorObserver = TestObserver.test(viewModel.getErrorData());loadingObserver = TestObserver.test(viewModel.getIsLoadingData());
loadingStates = loadingObserver.valueHistory();//Act
viewModel.fetchProductRecommendation(null);//Verify
loadingObserver.assertHistorySize(2);
assertEquals(true, loadingStates.get(0));
assertEquals(false, loadingStates.get(1));
assertEquals(throwable, errorObserver.value());
}@After
public void tearDown() throws Exception {
api = null;
}
}
Here we are expecting to load first then hide loading then we will show data or error.
Now let suppose someone changes in code and make a diffrent sequence like loading false first then loading true then data or data first then loading true then loading false. So in this scenario, Our test case will fail.
Thanks for reading. Soon I will post more articles on android, core java.
till then happy learning… keep reading… :)