Testing Presenter of MVP in Loklak Wok Android
Imagine working on a large source code, and as a new developer you are not sure whether the available source code works properly or not, you are surrounded by questions like, Are all these methods invoked properly or the number of times they need to be invoked? Being new to source code and checking manually already written code is a pain. For cases like these unit-tests are written. Unit-tests check whether the implemented code works as expected or not. This blog post explains about implementation of unit-tests of Presenter in a Model-View-Presenter (MVP) architecture in Loklak Wok Android. Adding Dependencies to project In app/build.gradle file defaultConfig { ... testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } dependencies { ... androidTestCompile 'org.mockito:mockito-android:2.8.47' androidTestCompile 'com.android.support:support-annotations:25.3.1' androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2' } Setup for Unit-Tests The presenter needs a realm database and an implementation of LoklakAPI interface. Along with that a mock of the View is required, so as to check whether the methods of View are called or not. The LoklakAPI interface can be mocked easily using Mockito, but the realm database can’t be mocked. For this reason an in-memory realm database is created, which will be destroyed once all unit-test are executed. As the presenter is required for each unit-test method we instantiate the in-memory database before all the tests start i.e. by annotating a public static method with @BeforeClass, e.g. setDb method. @BeforeClass public static void setDb() { Realm.init(InstrumentationRegistry.getContext()); RealmConfiguration testConfig = new RealmConfiguration.Builder() .inMemory() .name("test-db") .build(); mDb = Realm.getInstance(testConfig); } NOTE: The in-memory database should be closed once all unit-tests are executed. So, for closing the databasse we create a public static method annotated with @AfterClass, e.g. closeDb method. @AfterClass public static void closeDb() { mDb.close(); } Now, before each unit-test is executed we need to do some setup work like instantiating the presenter, a mock instance of API interface generated by using mock static method and pushing in some sample data into the database. Our presenter uses RxJava and RxAndroid which depend on IO scheduler and MainThread scheduler to perform tasks asynchronously and these schedulers are not present in testing environment. So, we override RxJava and RxAndroid to use trampoline scheduler in place of IO and MainThread so that our test don’t encounter NullPointerException. All this is done in a public method annotated with @Before e.g. setUp. @Before public void setUp() throws Exception { // mocking view and api mMockView = mock(SuggestContract.View.class); mApi = mock(LoklakAPI.class); mPresenter = new SuggestPresenter(mApi, mDb); mPresenter.attachView(mMockView); queries = getFakeQueries(); // overriding rxjava and rxandroid RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline()); RxAndroidPlugins.setMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline()); mDb.beginTransaction(); mDb.copyToRealm(queries); mDb.commitTransaction(); } Some fake suggestion queries are created which will be returned as observable when API interface is mocked. For this, simply two query objects are created and added to a List after their query parameter is set. This is implemented in getFakeQueries method. private List<Query> getFakeQueries() { List<Query> queryList = new ArrayList<>(); Query linux = new Query(); linux.setQuery("linux"); queryList.add(linux); Query india = new Query(); india.setQuery("india"); queryList.add(india); return queryList; } After that, a method is…
