Kotlin + RxJava + Dagger2 + MVP = Clean Architecture + Easy Unit Testing

Burak Eregar
AndroidPub
Published in
3 min readNov 29, 2017

Hi Everyone,

First of all, please find the code related to this article in the repo below.

I have created a sample project to use RxJava in Kotlin and tried to make it easy for unit testing. But I have faced some problems and I wanted to explain how have I achieved them.

First Problem: Mockito cannot mock/spy because : – final class

Reason: All classes are closed by default in Kotlin.

How to fix: We need to enable MockMaker plugin.

Firstly, If you are using Mockito you need to update it to Mockito2. After that, create ‘mockito-extensions’ package under resources. Then create a file named ‘org.mockito.plugins.MockMaker’ under ‘mockito-extensions’ package. Lastly, add the text ‘mock-maker-inline’ to the file.

It should be like the image below;

Schedulers

When it comes to the schedulers in testing we can follow two ways. First one is using RxAndroidPlugins and RxJavaPlugins and the second one is going for Scheduler Injection using Strategy Pattern. I am not going to dive deep into this but If you don’t have any idea about them you can read this article.

I preferred using Scheduler Injection in this project. I have created TestSchedulerProvider class which implements Scheduler Provider in the test package.

TestSchedulerProvider.kt

Syntax Problems While Using Mockito

I would recommend this library to achieve the most annoying syntax problems.

Basically, instead of writing this

val mockClass = mock(Foo::class.java)

we just need to write

val mockClass: Foo = mock()

The Problem With “When”

As “when” keyword is used by Kotlin we are not able to use it in our tests. We have to use it like `when`. It will take our time until we get used to it. However, we can also use “given” instead of “`when`”.

Let’s Test!

Testing Our Presenter

In the setup method, we need to provide the needed dependencies to our presenter. So, I created compositeDisposable and testSchedulerProvider to create HomePresenter. After creating the presenter, I have attached the mock view to the presenter.

Testing getRepos method From HomePresenter

fun getRepos(searchKey: String) {
view?.showProgress()
disposable.add(api.search(searchKey)
.subscribeOn(scheduler.io())
.observeOn(scheduler.ui())
.subscribe(
{ result ->
view?.hideProgress()
view?.onSearchResponse(result?.items)

if (result.items == null
|| result.items.isEmpty()) {
view?.noResult()
}
},
{ _ ->
view?.hideProgress()
view?.onError()
})
)
}

Steps

  • Verify showProgress method is called
  • Verify hideProgress method is called
  • Create test scenarios to ensure that onSearchResponse, noResult and onError methods are calling correctly.

Since our onAttach belongs to BasePresenter we shouldn’t test it on each presenter. We should test BasePresenter class instead.

Success Scenario

@Test
fun test_getRepos_should_callSuccess() {
val mockedResponse: RepoResponse = mock()
val searchKey = "test"

doReturn(Observable.just(mockedResponse))
.`when`(api)
.search(searchKey)

presenter.getRepos(searchKey)

testScheduler.triggerActions()

verify(view).showProgress()
verify(view).onSearchResponse(mockedResponse.items)
verify(view).hideProgress()

}

I mocked the response and added a rule which returns mockedResponse when API's “search” function called with “searchKey” param.

After that, I called presenter.getRepos function with the predefined searchKey. This function will trigger API's “search” function.

We have to trigger the actions if we use RxJava. So I have called testScheduler.triggerActions().

Now we can test our expectations. For this scenario verifying showProgress, hideProgress and onSearchResponse method are enough.

Other Scenarios

As I have mentioned almost every line of the tests I am pasting the test functions directly. I have followed the same path with the success scenario.

@Test
fun test_getRepos_shouldNot_callNoResult() {
val mockedResponse: RepoResponse = mock()
val items = ArrayList<RepoItem>()
val searchKey = "test"

items.add(Mockito.mock(RepoItem::class.java))

`when`(mockedResponse.items).thenReturn(items)

doReturn(Observable.just(mockedResponse))
.`when`(api)
.search(searchKey)

presenter.getRepos(searchKey)

testScheduler.triggerActions()

verify(view, times(0)).noResult()
}

@Test
fun test_getRepos_should_callNoResult() {
val mockedResponse: RepoResponse = mock()
val items = ArrayList<RepoItem>()
val searchKey = "test"

`when`(mockedResponse.items).thenReturn(items)

doReturn(Observable.just(mockedResponse))
.`when`(api)
.search(searchKey)

presenter.getRepos(searchKey)

testScheduler.triggerActions()

verify(view).noResult()
}

@Test
fun test_getRepos_should_callError() {
val mockedResponse: Throwable = mock()
val searchKey = "test"

doReturn(Observable.just(mockedResponse))
.`when`(api)
.search(searchKey)

presenter.getRepos(searchKey)

testScheduler.triggerActions()

verify(view).showProgress()
verify(view).onError()
verify(view).hideProgress()
}

The project also contains test classes for Shared Preferences Helper and BasePresenter.

Please find the code related to this article in the repo below.

If you don’t prefer using RxJava you can check the repo below.

If you liked the article, please clap it. Thanks!

--

--