how to test the server call with Mockito, Retrofit and RxJava

how to test the server call with Mockito, Retrofit and RxJava

In this example you can learn how to test server call with Mockito and RxJava
1. Service
2. ServiceInteractor
3. ServiceInteractorTest

Simple Service:

public interface Service {
    String URL_BASE = "https://guessthebeach.herokuapp.com/api/";

    @GET("topics/")
    Observable<List<Topics>> getTopicsRx();

}

For ServiceInteractor

public class ServiceInteractor {
    private LruCache<String, List<Topics>> cache;
    private Service service;

    public ServiceInteractor(Retrofit retrofit, LruCache<String, List<Topics>> cache) {
        this.cache = cache;
        this.service = retrofit.create(Service.class);
    }

    public Observable<List<Topics>> searchUsers() {
        return Observable.concat(cachedResults(), networkResults()).first();
    }

    private Observable<List<Topics>> cachedResults() {
        return Observable.just(cache.get("query"))
                .filter((List<Topics> result) ->
                        result != null
                );
    }

    private Observable<List<Topics>> networkResults() {
        return service.getTopicsRx()
                .doOnNext((List<Topics> result) ->
                        cache.put("query", result));
    }
}
 

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:

  1. Script the mocks.
  2. Run application code.
  3. Verify that the expected requests were made.

Here’s a complete example in ServiceInteractorTest:

 

public class ServiceInteractorTest {
    @Mock
    private LruCache<String, List<Topics>> mCache;

    @Before
    public void setup(){

        MockitoAnnotations.initMocks(this);
        when(mCache.get(anyString())).thenReturn(null);
    }

    @Test
    public void mockServiceTest() {

        Topics topics = new Topics(1, "football");

        List<Topics> result = new ArrayList();

        result.add(topics);

        MockWebServer mockService = new MockWebServer();
        mockService.enqueue(new MockResponse().setBody(new Gson().toJson(result)));

        Retrofit retrofit = new Retrofit.Builder()
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(mockService.url("dfdf/"))
                .build();

        TestSubscriber<List<Topics>> subscriber = new TestSubscriber<>();
        ServiceInteractor serviceInteractor = new ServiceInteractor(retrofit, mCache);
        serviceInteractor.searchUsers().subscribe(subscriber);

        subscriber.assertNoErrors();
        subscriber.assertCompleted();
    }

    @Test
    public void callServiceTest() {
        Topics topics = new Topics(1, "Discern The Beach");
        Topics topicsTwo = new Topics(2, "Discern The Football Player");

        List<Topics> result = new ArrayList();
        result.add(topics);
        result.add(topicsTwo);

        MockWebServer mockWebServer = new MockWebServer();
        mockWebServer.enqueue(new MockResponse().setBody(new Gson().toJson(result)));

        Retrofit retrofit = new Retrofit.Builder()
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(mockWebServer.url("https://guessthebeach.herokuapp.com/api/"))
                .build();

        TestSubscriber<List<Topics>> subscriber = new TestSubscriber<>();
        ServiceInteractor serviceInteractor = new ServiceInteractor(retrofit, mCache);
        serviceInteractor.searchUsers().subscribe(subscriber);

        subscriber.assertNoErrors();
        subscriber.assertCompleted();
    }


}

 

You can check my example in GitHub

Learn RxJava

Learn RxJava

In this example you can understand different elements of RxJava with Strings.

Simple
This method creates an Observable such that when an Observer subscribes, the onNext() of the Observer is immediately called with the argument provided to Observable.just(). The onCompleted() will then be called since the Observable has no other values to emit.

Observable.create(subcriber ->{
        subcriber.onNext("Hello");
        subcriber.onNext("Javier Gonzalez");
        subcriber.onCompleated();
    });

Now with Exception:

    Observable.create(subcriber ->{
        subcriber.onNext("Hello");
        subcriber.onNext("Javier Gonzalez");
        subcriber.onError(new Exception("iOS user now allowed"));
    });

And there are another different ways to do:

    Observable.just("Hello again...");

    Observable.from(Arrays.asList("Hello", "again..."));

    Observable.from(new String[]{"Hello", "again..."});

    Observable.concat(Observable.just("Hello"), Observable.just("again..."));

    Observable.merge(Observable.just("Hello", "again..."), Observable.never());

Also you can check this example
And this post

Carousel ViewPager

Carousel ViewPager

If you want a good tutorial in your apps, you can make a carousel.

We can use my favorite library for tutorial

    compile "com.romandanylyk:pageindicatorview:0.1.1"

Example in GitHub

In the layout We should add ViewPager and the Indicator.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:attrs="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.thedeveloperworldisyours.carouselviewpager.MainActivity">


    <android.support.v4.view.ViewPager
        android:id="@+id/activity_main_view_pager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <com.rd.PageIndicatorView
        android:id="@+id/tutorial_activity_page_indicator_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="10dp"
        attrs:piv_animationType="drop"
        attrs:piv_dynamicCount="true"
        attrs:piv_interactiveAnimation="true"
        attrs:piv_padding="16dp"
        attrs:piv_radius="8dp"
        attrs:piv_selectedColor="@color/colorAccent"
        attrs:piv_unselectedColor="@color/colorPrimaryDark"
        attrs:piv_viewPager="@id/activity_main_view_pager" />

</RelativeLayout>

We can custom the item

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <com.thedeveloperworldisyours.carouselviewpager.CustomLinearLayout
        android:id="@+id/item_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:id="@+id/item_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="@dimen/item_size_text" />

        <Button
            android:id="@+id/item_content"
            android:layout_width="230dp"
            android:layout_height="120dp"
            android:background="@android:color/black"/>
    </com.thedeveloperworldisyours.carouselviewpager.CustomLinearLayout>
</LinearLayout>
public class CustomFragment extends Fragment {

    public static Fragment newInstance(Activity context, int position, float scale) {
        Bundle bundle = new Bundle();
        bundle.putInt("position", position);
        bundle.putFloat("scale", scale);
        return Fragment.instantiate(context, CustomFragment.class.getName(), bundle);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        if (container == null) {
            return null;
        }

        LinearLayout linearLayout = (LinearLayout)
                inflater.inflate(R.layout.item, container, false);

        int position = this.getArguments().getInt("position");
        TextView textView = (TextView) linearLayout.findViewById(R.id.item_text);
        textView.setText(String.valueOf(position));

        CustomLinearLayout root = (CustomLinearLayout) linearLayout.findViewById(R.id.item_root);
        float scale = this.getArguments().getFloat("scale");
        root.setScaleBoth(scale);

        return linearLayout;
    }
}
public class CustomLinearLayout extends LinearLayout {
    private float mScale = BIG_SCALE;

    public CustomLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomLinearLayout(Context context) {
        super(context);
    }

    public void setScaleBoth(float scale) {
        this.mScale = scale;
        this.invalidate();    // If you want to see the mScale every time you set
        // mScale you need to have this line here,
        // invalidate() function will call onDraw(Canvas)
        // to redraw the view for you
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // The main mechanism to display mScale animation, you can customize it
        // as your needs
        int w = this.getWidth();
        int h = this.getHeight();
        canvas.scale(mScale, mScale, w / 2, h / 2);

        super.onDraw(canvas);
    }
}
public class CustomPagerAdapter extends FragmentPagerAdapter implements ViewPager.PageTransformer {
    public final static float BIG_SCALE = 1.0f;
    public final static float SMALL_SCALE = 0.7f;
    public final static float DIFF_SCALE = BIG_SCALE - SMALL_SCALE;

    private Activity mContext;
    private FragmentManager mFragmentManager;
    private float mScale;

    public CustomPagerAdapter(Activity context, FragmentManager fragmentManager) {
        super(fragmentManager);
        this.mFragmentManager = fragmentManager;
        this.mContext = context;
    }

    @Override
    public Fragment getItem(int position) {
        // make the first mViewPager bigger than others
        if (position == FIRST_PAGE)
            mScale = BIG_SCALE;
        else
            mScale = SMALL_SCALE;

        return CustomFragment.newInstance(mContext, position, mScale);
    }

    @Override
    public int getCount() {
        return PAGES;
    }

    @Override
    public void transformPage(View page, float position) {
        CustomLinearLayout myLinearLayout = (CustomLinearLayout) page.findViewById(R.id.item_root);
        float scale = BIG_SCALE;
        if (position > 0) {
            scale = scale - position * DIFF_SCALE;
        } else {
            scale = scale + position * DIFF_SCALE;
        }
        if (scale < 0) scale = 0;
        myLinearLayout.setScaleBoth(scale);
    }
}
public class MainActivity extends AppCompatActivity {

    public final static int PAGES = 5;
    public final static int FIRST_PAGE = 0  ;

    public CustomPagerAdapter mAdapter;
    public ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mViewPager = (ViewPager) findViewById(R.id.activity_main_view_pager);

        mAdapter = new CustomPagerAdapter(this, this.getSupportFragmentManager());
        mViewPager.setAdapter(mAdapter);
        mViewPager.setPageTransformer(false, mAdapter);

        // Set current item to the middle page so we can fling to both
        // directions left and right
        mViewPager.setCurrentItem(FIRST_PAGE);

        // Necessary or the mViewPager will only have one extra page to show
        // make this at least however many pages you can see
        mViewPager.setOffscreenPageLimit(3);

        // Set margin for pages as a negative number, so a part of next and
        // previous pages will be showed
        mViewPager.setPageMargin(-400);
    }
}

Example in GitHub