0

I am developing a toy Android app using Kotlin (Coroutine).

I am familiar with Rx more than Coroutine.

I feel that Coroutine difficult more than Rx.

Anyway here is my Presenter code:

class NowPlayingPresenter(
    private val view: NowPlayingContract.View,
    private val getMovies: GetNowPlayingMovies,
    private val uiContext: CoroutineContext = Dispatchers.Main,
    ioContext: CoroutineContext = Dispatchers.IO
) : NowPlayingContract.Presenter, CoroutineScope, AnkoLogger {

    override val coroutineContext: CoroutineContext = Job() + ioContext

    override fun unsubscribe() {
        coroutineContext.cancel()
    }

    override fun getMoviesNowPlaying() {
        view.showProgressBar(View.VISIBLE)
        view.hideError()

        launch {
            try {
                val movies = getMovies.get()

                // 'movies' is always null!!! I don't know why...

                withContext(uiContext) {
                    view.showProgressBar(View.GONE)
                    if (movies.isNullOrEmpty()) {
                        view.onError(R.string.err_movies_not_exists)
                    } else {
                        view.onMoviesLoaded(movies)
                    }
                }
            } catch (t: Throwable) {
                view.showProgressBar(View.GONE)
                view.onError(R.string.err_get_movies_failed)
                error("[Y.M.] getMoviesNowPlaying - failed: ${t.message}", t)
            }
        }
    }

}

Here is my GetNowPlayingMovies code, it is just interface:

interface GetNowPlayingMovies {
    suspend fun get(): List<SimpleMovie>
}

And below is my JUnit test code:

class MyDataPresenterTest {

    @Mock
    private lateinit var mockView: NowPlayingContract.View

    @Mock
    private lateinit var getMovies: GetNowPlayingMovies

    private lateinit var presenter: NowPlayingPresenter

    private lateinit var inOrder: InOrder

    private val mockMovie1 = SimpleMovie("posterpath1", false, "2019-03-01", 10, "hello world1", 10f)
    private val mockMovie2 = SimpleMovie("posterpath2", true, "2019-03-02", 20, "hello world2", 9f)
    private val mockMovie3 = SimpleMovie("posterpath3", false, "2019-03-03", 30, "hello world3", 8f)
    private val mockMovie4 = SimpleMovie("posterpath4", false, "2019-03-04", 40, "hello world4", 7f)
    private val mockMovie5 = SimpleMovie("posterpath5", false, "2019-03-05", 50, "hello world5", 6f)
    private val mockMovies: List<SimpleMovie> = listOf(
        mockMovie1,
        mockMovie2,
        mockMovie3,
        mockMovie4,
        mockMovie5
    )

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)

        inOrder = Mockito.inOrder(mockView)

        presenter = NowPlayingPresenter(mockView, getMovies, Dispatchers.Unconfined, Dispatchers.Unconfined)
    }

    @Test
    fun getMoviesNowPlayingTest() = runBlocking {
        `when`(getMovies.get()).thenReturn(mockMovies)

        presenter.getMoviesNowPlaying()

        inOrder.verify(mockView).showProgressBar(View.VISIBLE)
        inOrder.verify(mockView).hideError()
        inOrder.verify(mockView).showProgressBar(View.GONE)
        inOrder.verify(mockView).onMoviesLoaded(mockMovies)
    }

}

Somebody help me, please?

Here is my full code: https://github.com/yoonhok524/Android-Sandbox/tree/master/kotlin-coroutine

I tried to apply 'Clean architecture' in this project, so the code is not easy to read... maybe...

4
  • It would be useful if you post creating mockData object code. Commented Jun 5, 2019 at 5:43
  • can you share the message of the failed test? Commented Jun 5, 2019 at 5:45
  • r2rek, I already wrote the error message in the Presenter code as a comment. Commented Jun 5, 2019 at 5:52
  • Ircover, It just simple list of data class, when I check it in the test code, it was not null. Commented Jun 5, 2019 at 5:53

2 Answers 2

1

You are using Mockito 1.x. If you make the mock for suspend function, use the lastest Mockito. https://stackoverflow.com/a/53101077/4639261

Sign up to request clarification or add additional context in comments.

Comments

0

In my case and conventions....

  1. implement 'CoroutineScope' and override coroutineContext
  2. coroutineContext = Job() + Dispatchers (need job.cancel() because cancel CoroutineScope and childs)
  3. Use launch and withContext(...)

        launch {
            val data = withContext(ioContext) { 
              repository.get(id)
            }
             
            withContext(uiContext) {
                view?.onDataLoaded(data)
            }
        }

1 Comment

I applied your code, but it failed with error message: Exception in thread "main @coroutine#1" java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.