4
\$\begingroup\$

I am using MVVM pattern with databinding. I have written tests. But I want them reviewed. The test is related to JUnit test on the ViewModel.

FeedViewModelTest.java

@RunWith(PowerMockRunner.class)
@PrepareForTest({Observable.class, AndroidSchedulers.class})
@PowerMockIgnore("javax.net.ssl.*")
public class FeedViewModelTest {
 FeedViewModel feedViewModel;
 DataManager dataManager;
 FeedViewModel.DataListener dataListener;
 FeedApi feedApi;
 @Before
 public void setUp() {
 dataListener = mock(FeedViewModel.DataListener.class);
 Context mMockContext = mock(Context.class);
 dataManager =mock(DataManager.class);
 feedApi = mock(FeedApi.class);
 feedViewModel = spy(new FeedViewModel(mMockContext, dataListener));
 }
 @Test
 public void testShouldScheduleLoadFromAPIOnBackgroundThread() {
 Observable<FeedResponse> observable = (Observable<FeedResponse>) mock(Observable.class);
 when(dataManager.fetchFeed()).thenReturn(observable);
 when(observable.subscribeOn(Schedulers.io())).thenReturn(observable);
 when(observable.observeOn(AndroidSchedulers.mainThread())).thenReturn(observable);
 //call test method
 feedViewModel.fetchFeed();
 verify(feedViewModel).fetchFeed();
 TestSubscriber<FeedResponse> testSubscriber = new TestSubscriber<>();
 observable.subscribeOn(Schedulers.io());
 observable.observeOn(AndroidSchedulers.mainThread());
 observable.subscribeWith(new DisposableObserver<FeedResponse>() {
 @Override
 public void onNext(FeedResponse value) {
 dataListener.onDataChanged(value.getData());
 }
 @Override
 public void onError(Throwable e) {
 e.printStackTrace();
 dataListener.onError();
 }
 @Override
 public void onComplete() {
 }
 });
 //verify if all methods in the chain are called with correct arguments
 verify(observable).subscribeOn(Schedulers.io());
 verify(observable).observeOn(AndroidSchedulers.mainThread());
 verify(observable).subscribeWith(Matchers.<DisposableObserver<FeedResponse>>any());
 }
}

FeedViewModel.java

 public class FeedViewModel {
 public DataManager dataManager;
 private DataListener datalistener;
 private Context mContext;
 public FeedViewModel(Context context, DataListener datalistener) {
 this.datalistener = datalistener;
 mContext = context;
 dataManager = new DataManager();
 }
 public void fetchFeed() {
 dataManager.fetchFeed()
 .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribeWith(new DisposableObserver<FeedResponse>() {
 @Override
 public void onError(Throwable e) {
 e.printStackTrace();
 datalistener.onError();
 }
 @Override
 public void onComplete() {
 }
 @Override
 public void onNext(FeedResponse feedResponse) {
 if(datalistener!=null) {
 datalistener.onDataChanged(feedResponse.getData());
 }
 }
 });
 }
 public interface DataListener
 {
 void onDataChanged(List<FeedModel> model);
 void onError();
 }
 }

DataManager.java

public class DataManager {
 private FeedApi feedApi;
 private Observable<FeedResponse> feedResponseObservable;
 public FeedApi getFeedApi() {
 return feedApi;
 }
 public DataManager()
 {
 feedApi = new FeedApi();
 }
 public Observable<FeedResponse> fetchFeed() {
 feedResponseObservable = feedApi.fetchFeed(1);
 return feedResponseObservable;
 }
}

Testing libs used:

testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.9.5'
testCompile 'org.powermock:powermock-api-mockito:1.5.6'
testCompile 'org.powermock:powermock-module-junit4:1.6.2'

DataListener is an interface and is used as a callback to the activity. I have some views in activity that are visible or gone based on the data fetched from the server. Is my unit test for FeedViewModel correct?

Note: The test for FeedViewModelTest passes. I am using the RxJava2.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Sep 30, 2016 at 15:40
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

It's been a while and I haven't got a review for the question. I am attempting to answer my own question. Do point out any mistakes. Happy to take it.

  1. Create Mocks

  2. Define return values for methods with mockitos when

  3. Call the test method

  4. Verify all the methods are called in the chain with correct arguments

I changed my FeedViewModel constructor

public FeedViewModel(Context context, DataListener datalistener,DataManager dataManager) {
 this.datalistener = datalistener;
 mContext = context;
 this.dataManager = dataManager;
}

With the above I can pass actual arguments while fetching data and mock them while testing. Mockitos when requires mock objects.

With that in place, I had to pass the mocks while setting up in the test

Mocks:

 dataListener = mock(FeedViewModel.DataListener.class);
 Context mMockContext = mock(Context.class);
 dataManager =mock(DataManager.class);

Then:

feedViewModel = spy(new FeedViewModel(mMockContext, dataListener,dataManager));

Then my final unit test:

@Test
public void testShouldScheduleLoadFromAPIOnBackgroundThread() {
 Observable<FeedResponse> observable = (Observable<FeedResponse>) mock(Observable.class);
 when(dataManager.fetchFeed()).thenReturn(observable);
 when(observable.subscribeOn(Schedulers.io())).thenReturn(observable);
 when(observable.observeOn(AndroidSchedulers.mainThread())).thenReturn(observable);
 //call test method
 feedViewModel.fetchFeed();
 verify(feedViewModel).fetchFeed();
 verify(observable).subscribeOn(Schedulers.io());
 verify(observable).observeOn(AndroidSchedulers.mainThread());
 verify(observable).subscribeWith(Matchers.<DisposableObserver<FeedResponse>>any());
}

To be sure that my code is correct, I checked some repos on github. Although I used MVVM pattern the test case is similar when you use MVP.

To test you need to have a clean architecture. With clear separation you can test your viewmodel with junit test and the UI with espresso.

I referred to this github repository to make sure my test code is correct.

Stephen Rauch
4,31412 gold badges24 silver badges36 bronze badges
answered Oct 18, 2016 at 5:51
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.