I'm trying to understand how should I test my app, I'm still learning with mockito
I also saw mockk
but couldn't make it work, this is my Presenter
class MyPresenterImpl @Inject constructor(var myUseCase: MyUseCase) : MyContract.Presenter {
private var mView: MyContract.View? = null
private var disposable: Disposable? = null
override fun attachView(view: MyContract.View) {
this.mView = view
this.mView?.showProgressBar(true)
}
override fun loadResults() {
disposable = getList.execute()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result ->
mView?.showProgressBar(false)
mView?.showResults(result)
},
{ error ->
mView?.showProgressBar(false)
mView?.showError(error.localizedMessage)
})
}
override fun rxJavaUnsuscribe() {
if (disposable != null && !disposable!!.isDisposed) {
disposable!!.dispose()
}
}
override fun detachView() {
this.mView = null
}
}
How I'm supposed to test this presenter? Do I have to add all of these methods?
I was trying to do it with mockito
but I also can use mockk
.
And some people told me that I have to do something with Schedulers
and use the trampoline
one but it's not clear for me could anyone of you provide an example or explain it a little bit?
If I understood your question correctly then you're trying to understand how to implement a complete MVP patter with Unit tests (using Mockito).
I've written a sample code (app which displays list of books) which explains a basic MVP implementation of same with one JUnit test case here : https://github.com/harneev/AndroidMVPkt
Lets talk a little about classes here:
ViewContract.kt
- Interface defining methods which guide dumb views to perform an action.ModelContract.kt
- Interface defining methods to fetch data (either from database or from server) which will be encapsulated under implementation class.Presenter.kt
- Handles all business logic and orchestrate this logic through concrete View and Model injected as parameters.Note: Presenter being a regular class and business logic orchestrator is dependent on both model and views. Some developers like to add
Presenter
reference toView
interface but it is cleaner this ways.
Now coming to unit test cases (PresenterTest.kt
) for this MVP design.
I'm using mockito-kotlin as mocking framework for better kotlin support.
I've only added one test case in this scenario named test if books are displayed
() which mocks ViewContract
and ModelContract
and initializes Presenter
. Finally Mockito.verify
method verifies if view received the list of books generated by model.
For better unit test cases I always break it down to following three scenarios which are explained as below:
// 1. given
how mocks will behave when their respective methods are called
// 2. when
when execution method is called
// 3. then
verify / assert if required action is performed
Hope this helps.
Create custom rule TestSchedulerRule.kt for junit tests in your test package
class TestSchedulerRule(private val scheduler: Scheduler = Schedulers.trampoline()) : TestRule {
override fun apply(base: Statement, d: Description): Statement {
return object : Statement() {
override fun evaluate() {
RxJavaPlugins.setIoSchedulerHandler { scheduler }
RxJavaPlugins.setComputationSchedulerHandler { scheduler }
RxJavaPlugins.setNewThreadSchedulerHandler { scheduler }
RxJavaPlugins.setSingleSchedulerHandler { scheduler }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { scheduler }
RxAndroidPlugins.setMainThreadSchedulerHandler { scheduler }
try {
base.evaluate()
} finally {
RxJavaPlugins.reset()
RxAndroidPlugins.reset()
}
}
}
}
}
Create MyPresenterImplTest for your presenter and write unit-tests you needed with created rule. For example i added one test for presenter logic with kotlin-mockito and junit4.
@RunWith(MockitoJUnitRunner::class)
class MyPresenterImplTest {
@Rule
@JvmField
val testRule = TestSchedulerRule()
private val view: MyContract.View = mock()
private val myUseCase: MyUseCase = mock()
private val presenter = MyPresenterImpl(myUseCase)
@Before
fun setUp() {
presenter.attachView(view)
}
@Test
fun `WHEN btnLoad clicked EXPECT load and show results`() {
//create needed results
val results = listOf<Any>()
//mock the result of execution myUseCase.invoke()
whenever(myUseCase.invoke()).thenReturn(Single.just(results))
//trigger the needed action
presenter.loadResults()
//verify that all needed actions were called
verify(myUseCase).invoke()
verify(view).showResults(results)
}
}
Explanation about Rule.
We need to create custom test rule because the default scheduler returned by AndroidSchedulers.mainThread() (when you write .observeOn(AndroidSchedulers.mainThread() in your presenter) is an instance of LooperScheduler and relies on Android dependencies that are not available in JUnit tests.
So we need initializing RxAndroidPlugins with a different Scheduler before the tests runs. Using rule allows you to reuse the initialization logic across multiple test classes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With