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.

\$\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.

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.