简体   繁体   中英

How to test a presenter MVP Android

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:

  1. ViewContract.kt - Interface defining methods which guide dumb views to perform an action.
  2. ModelContract.kt - Interface defining methods to fetch data (either from database or from server) which will be encapsulated under implementation class.
  3. 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 to View 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.

  1. 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() } } } } }
  2. 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.

To write a unit test for your presenter, you should: 1. mock myUseCase : private val myUseCase = mock<MyUseCase>() 2. add Schedulers.io() and AndroidSchedulers.mainThread() to the constructor of the presenter then you can set Schedulers.trampoline()` when you create a presenter object for testing:

    class MyPresenterImpl @Inject constructor(
        val myUseCase: MyUseCase,
        val ioScheduler: Scheduler = Schedulers,
        val uiScheduler: Scheduler = AndroidSchedulers.mainThread()
    ) : MyContract.Presenter

then in the seUp() of your test:

    presenter = MyPresenterImpl(myUseCase, Schedulers.trampoline(), Schedulers.trampoline())
  1. stub the function execute() of the use case:
    myUseCase.stub {
        on { execute() }.thenReturn(Single.just(xyz))
    }
  1. verify your code

notes: I prefer to use the Mockito version of Nhaarman

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM