【问题标题】:Android Coroutine MVP junit test code failedAndroid Coroutine MVP junit 测试代码失败
【发布时间】:2019-06-05 04:58:57
【问题描述】:

我正在使用 Kotlin(协程)开发一个玩具 Android 应用。

比起 Coroutine,我更熟悉 Rx。

我觉得 Coroutine 比 Rx 更难。

这里是我的演示者代码:

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 目标代码会很有用。
  • 可以分享一下测试失败的消息吗?
  • r2rek,我已经在 Presenter 代码中写了错误消息作为注释。
  • Ircover,它只是简单的数据类列表,当我在测试代码中检查它时,它不为空。

标签: android kotlin junit coroutine clean-architecture


【解决方案1】:

您正在使用 Mockito 1.x。如果您为挂起功能制作模拟,请使用最新的 Mockito。 https://stackoverflow.com/a/53101077/4639261

【讨论】:

    【解决方案2】:

    在我的情况和约定......

    1. 实现“CoroutineScope”并覆盖 coroutineContext
    2. coroutineContext = Job() + Dispatchers (需要 job.cancel() 因为取消 CoroutineScope 和孩子)
    3. 使用 launch 和 withContext(...)

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

    【讨论】:

    • 我应用了您的代码,但它失败并显示错误消息:线程“main @coroutine#1”java.lang.IllegalStateException 中的异常:带有主调度程序的模块无法初始化。对于测试,可以使用来自 kotlinx-coroutines-test 模块的 Dispatchers.setMain
    猜你喜欢
    • 1970-01-01
    • 2011-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-29
    • 2019-05-02
    • 2015-07-04
    相关资源
    最近更新 更多