Не удалось выполнить тестовый код Android Coroutine MVP junit

Я разрабатываю игрушечное приложение для Android, используя Kotlin (Coroutine).

Я знаком с Rx больше, чем с Coroutine.

Я чувствую, что Coroutine сложнее, чем Rx.

Во всяком случае, вот мой код Presenter:

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)
            }
        }
    }

}

Вот мой код GetNowPlayingMovies, это просто интерфейс:

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

И ниже мой тестовый код JUnit:

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)
    }

}

Кто-нибудь, помогите мне, пожалуйста?

Вот мой полный код: https://github.com/yoonhok524/Android-Sandbox/tree/master/kotlin-coroutine

Я попытался применить «Чистую архитектуру» в этом проекте, поэтому код не легко читать... может быть...

Было бы полезно, если бы вы разместили создание mockData объектного кода.

Ircover 05.06.2019 07:43

Вы можете поделиться сообщением о неудачном тесте?

r2rek 05.06.2019 07:45

r2rek, я уже написал сообщение об ошибке в коде Presenter в качестве комментария.

yoonhok 05.06.2019 07:52

Ircover, Это просто список класса данных, когда я проверял его в тестовом коде, он не был нулевым.

yoonhok 05.06.2019 07:53
0
4
1 269
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В моем случае и условностях....

  1. реализовать CoroutineScope и переопределить coroutineContext
  2. coroutineContext = Job() + Диспетчеры (нужно job.cancel(), потому что отменить CoroutineScope и дочерние элементы)
  3. Используйте запуск и withContext(...)

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

Я применил ваш код, но это не удалось с сообщением об ошибке: Исключение в потоке "main @coroutine # 1" java.lang.IllegalStateException: не удалось инициализировать модуль с диспетчером Main. Для тестов можно использовать Dispatchers.setMain из модуля kotlinx-coroutines-test

yoonhok 05.06.2019 08:23
Ответ принят как подходящий

Вы используете Mockito 1.x. Если вы делаете макет для функции приостановки, используйте последний Mockito. https://stackoverflow.com/a/53101077/4639261

Другие вопросы по теме