Unit testing android application with retrofit and rxjava

前端 未结 6 389
旧巷少年郎
旧巷少年郎 2020-12-08 16:13

I have developed an android app that is using retrofit with rxJava, and now I\'m trying to set up the unit tests with Mockito but I don\'t know how to mock the api responses

6条回答
  •  萌比男神i
    2020-12-08 16:43

    I use these classes:

    1. Service
    2. RemoteDataSource
    3. RemoteDataSourceTest
    4. TopicPresenter
    5. TopicPresenterTest

    Simple Service:

    public interface Service {
        String URL_BASE = "https://guessthebeach.herokuapp.com/api/";
    
        @GET("topics/")
        Observable> getTopicsRx();
    
    }
    

    For RemoteDataSource

    public class RemoteDataSource implements Service {
    
        private Service api;
    
        public RemoteDataSource(Retrofit retrofit) {
    
    
            this.api = retrofit.create(Service.class);
        }
    
    
        @Override
        public Observable> getTopicsRx() {
            return api.getTopicsRx();
        }
    }
    

    The key is MockWebServer from okhttp3.

    This library makes it easy to test that your app Does The Right Thing when it makes HTTP and HTTPS calls. It lets you specify which responses to return and then verify that requests were made as expected.

    Because it exercises your full HTTP stack, you can be confident that you’re testing everything. You can even copy & paste HTTP responses from your real web server to create representative test cases. Or test that your code survives in awkward-to-reproduce situations like 500 errors or slow-loading responses.

    Use MockWebServer the same way that you use mocking frameworks like Mockito:

    Script the mocks. Run application code. Verify that the expected requests were made. Here’s a complete example in RemoteDataSourceTest:

    public class RemoteDataSourceTest {
    
        List mResultList;
        MockWebServer mMockWebServer;
        TestSubscriber> mSubscriber;
    
        @Before
        public void setUp() {
            Topics topics = new Topics(1, "Discern The Beach");
            Topics topicsTwo = new Topics(2, "Discern The Football Player");
            mResultList = new ArrayList();
            mResultList.add(topics);
            mResultList.add(topicsTwo);
    
            mMockWebServer = new MockWebServer();
            mSubscriber = new TestSubscriber<>();
        }
    
        @Test
        public void serverCallWithError() {
            //Given
            String url = "dfdf/";
            mMockWebServer.enqueue(new MockResponse().setBody(new Gson().toJson(mResultList)));
            Retrofit retrofit = new Retrofit.Builder()
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl(mMockWebServer.url(url))
                    .build();
            RemoteDataSource remoteDataSource = new RemoteDataSource(retrofit);
    
            //When
            remoteDataSource.getTopicsRx().subscribe(mSubscriber);
    
            //Then
            mSubscriber.assertNoErrors();
            mSubscriber.assertCompleted();
        }
    
        @Test
        public void severCallWithSuccessful() {
            //Given
            String url = "https://guessthebeach.herokuapp.com/api/";
            mMockWebServer.enqueue(new MockResponse().setBody(new Gson().toJson(mResultList)));
            Retrofit retrofit = new Retrofit.Builder()
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl(mMockWebServer.url(url))
                    .build();
            RemoteDataSource remoteDataSource = new RemoteDataSource(retrofit);
    
            //When
            remoteDataSource.getTopicsRx().subscribe(mSubscriber);
    
            //Then
            mSubscriber.assertNoErrors();
            mSubscriber.assertCompleted();
        }
    
    }
    

    You can check my example in GitHub and this tutorial.

    Also In the presenter you can see my server call with RxJava:

    public class TopicPresenter implements TopicContract.Presenter {
    
        @NonNull
        private TopicContract.View mView;
    
        @NonNull
        private BaseSchedulerProvider mSchedulerProvider;
    
        @NonNull
        private CompositeSubscription mSubscriptions;
    
        @NonNull
        private RemoteDataSource mRemoteDataSource;
    
    
        public TopicPresenter(@NonNull RemoteDataSource remoteDataSource, @NonNull TopicContract.View view, @NonNull BaseSchedulerProvider provider) {
            this.mRemoteDataSource = checkNotNull(remoteDataSource, "remoteDataSource");
            this.mView = checkNotNull(view, "view cannot be null!");
            this.mSchedulerProvider = checkNotNull(provider, "schedulerProvider cannot be null");
    
            mSubscriptions = new CompositeSubscription();
    
            mView.setPresenter(this);
        }
    
        @Override
        public void fetch() {
    
            Subscription subscription = mRemoteDataSource.getTopicsRx()
                    .subscribeOn(mSchedulerProvider.computation())
                    .observeOn(mSchedulerProvider.ui())
                    .subscribe((List listTopics) -> {
                                mView.setLoadingIndicator(false);
                                mView.showTopics(listTopics);
                            },
                            (Throwable error) -> {
                                try {
                                    mView.showError();
                                } catch (Throwable t) {
                                    throw new IllegalThreadStateException();
                                }
    
                            },
                            () -> {
                            });
    
            mSubscriptions.add(subscription);
        }
    
        @Override
        public void subscribe() {
            fetch();
        }
    
        @Override
        public void unSubscribe() {
            mSubscriptions.clear();
        }
    
    }
    

    And now TopicPresenterTest:

    @RunWith(MockitoJUnitRunner.class)
    public class TopicPresenterTest {
    
        @Mock
        private RemoteDataSource mRemoteDataSource;
    
        @Mock
        private TopicContract.View mView;
    
        private BaseSchedulerProvider mSchedulerProvider;
    
        TopicPresenter mThemePresenter;
    
        List mList;
    
        @Before
        public void setup() {
            MockitoAnnotations.initMocks(this);
    
            Topics topics = new Topics(1, "Discern The Beach");
            Topics topicsTwo = new Topics(2, "Discern The Football Player");
            mList = new ArrayList<>();
            mList.add(topics);
            mList.add(topicsTwo);
    
            mSchedulerProvider = new ImmediateSchedulerProvider();
            mThemePresenter = new TopicPresenter(mRemoteDataSource, mView, mSchedulerProvider);
    
    
        }
    
        @Test
        public void fetchData() {
    
            when(mRemoteDataSource.getTopicsRx())
                    .thenReturn(rx.Observable.just(mList));
    
            mThemePresenter.fetch();
    
            InOrder inOrder = Mockito.inOrder(mView);
            inOrder.verify(mView).setLoadingIndicator(false);
            inOrder.verify(mView).showTopics(mList);
    
        }
    
        @Test
        public void fetchError() {
    
            when(mRemoteDataSource.getTopicsRx())
                    .thenReturn(Observable.error(new Throwable("An error has occurred!")));
            mThemePresenter.fetch();
    
            InOrder inOrder = Mockito.inOrder(mView);
            inOrder.verify(mView).showError();
            verify(mView, never()).showTopics(anyList());
        }
    
    }
    

    You can check my example in GitHub and this article.

提交回复
热议问题