【问题标题】:Unit testing coroutines on UI threadUI线程上的单元测试协程
【发布时间】:2018-05-26 15:43:51
【问题描述】:

我正在使用协程在拉取刷新时执行异步调用,如下所示:

class DataFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {

    // other functions here

    override fun onRefresh() {
        loadDataAsync()
    }

    private fun loadDataAsync() = async(UI) {
        swipeRefreshLayout?.isRefreshing = true
        progressLayout?.showContent()

        val data = async(CommonPool) {
            service?.getData() // suspending function
        }.await()

        when {
            data == null -> showError()
            data.isEmpty() -> progressLayout?.showEmpty(null, parentActivity?.getString(R.string.no_data), null)
            else -> {
                dataAdapter?.updateData(data)
                dataAdapter?.notifyDataSetChanged()
                progressLayout?.showContent()
            }
        }

        swipeRefreshLayout?.isRefreshing = false
    }
}

当我实际将它放在设备上时,这里的一切都很好。我的错误、空、数据状态都处理的很好,性能也不错。但是,我也在尝试使用 Spek 对其进行单元测试。我的 Spek 测试如下所示:

@RunWith(JUnitPlatform::class)
class DataFragmentTest : Spek({

    describe("The DataFragment") {

        var uut: DataFragment? = null

        beforeEachTest {
            uut = DataFragment()
        }

        // test other functions

        describe("when onRefresh") {
            beforeEachTest {
                uut?.swipeRefreshLayout = mock()
                uut?.onRefresh()
            }

            it("sets swipeRefreshLayout.isRefreshing to true") {
                verify(uut?.swipeRefreshLayout)?.isRefreshing = true // says no interaction with mock
            }
        }
    }           
}

测试失败,因为它说没有与 uut?.swipeRefreshLayout 模拟的交互。经过一些实验,这似乎是因为我正在通过async(UI) 使用 UI 上下文。如果我将其设置为常规异步,我可以通过测试,但随后应用程序崩溃,因为我正在修改 UI 线程之外的视图。

任何想法为什么会发生这种情况?另外,如果有人对此有任何更好的建议,这将使其更具可测试性,我会全力以赴。

谢谢。

编辑:忘了提到我还尝试将verifyuut?.onRefresh() 包装在runBlocking 中,但我仍然没有成功。

【问题讨论】:

    标签: android unit-testing kotlin kotlinx.coroutines


    【解决方案1】:

    如果您想让事情变得干净并考虑在未来使用 MVP 架构,您应该了解 CourutineContext 是外部依赖项,应该通过 DI 注入或传递给您的演示者。 More details on topic.

    您的问题的答案很简单,您应该只使用 Unconfined CourutineContext 进行测试。 (more) 为了简单起见,创建一个对象,例如注入:

    package com.example
    
    object Injection {
        val uiContext : CourutineContext = UI
        val bgContext : CourutineContext = CommonPool
    }
    

    测试包中创建完全相同的对象,但更改为:

    package com.example
    
    object Injection {
        val uiContext : CourutineContext = Unconfined
        val bgContext : CourutineContext = Unconfined
    }
    

    在你的班级里面会是这样的:

    val data = async(Injection.bgContext) {service?.getData()}.await()
    

    【讨论】:

      猜你喜欢
      • 2021-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-18
      • 1970-01-01
      • 2010-09-30
      • 1970-01-01
      相关资源
      最近更新 更多