单元测试使用kotlin协同程序的改装调用

时间:2018-01-04 08:52:04

标签: android unit-testing kotlinx.coroutines

我正在使用kotlin coroutineskotlin retrofit coroutines在我目前正在处理的项目中执行网络请求。但是我无法弄清楚如何为逻辑通道进行单元测试。

这是我的代码:

class WorklistInteractor @Inject
    constructor(private val worklistRepository: WorklistRepository,
        private val preferenceManager: PreferenceManager)
: NetworkInteractor, WorklistDialogContract.Interactor {

    private var job = Job()

    override fun getWorklist(listener: OnWorklistResultListener) {
        job = launch(UI) {
            val result = async {
                worklistRepository.getWorklist(
                    ip = preferenceManager.worklistIp,
                    port = preferenceManager.worklistPort).awaitResult()
            }.await()

            when (result) {
            //Successful HTTP result
                is Result.Ok -> listener.onWorklistResult(result.value)
            // Any HTTP error
                is Result.Error -> {
                    Timber.e(result.exception, "HTTP error with code %s}", result.exception.code())
                    when(result.exception.code()) {
                        401 -> listener.onInvalidCredentialsFailure()
                        500 -> listener.internalServerError()
                        503 -> listener.noServerResponseFailure()
                        else -> listener.onError(result.exception.cause.toString())
                    }
                }
            // Exception while request invocation
                is Result.Exception -> {
                    Timber.e(result.exception.cause, "Exception with cause %s", result.exception.cause.toString())
                    when(result.exception) {
                        is ConnectException -> listener.connectionRefused()
                        is SocketTimeoutException -> listener.failedToConnectToHost()
                        else -> listener.onError(result.exception.cause.toString())
                    }
                }
            }
        }
    }

    override fun cancel() {
        job.cancel()
    }
}

这是我的单身测试之一:

@Test
fun `when worklistquery returns result, pass result back through listeners onWorklistResult`()
        = runBlocking {

    whenever(mWorklistRepositoryMock.getWorklist(anyString(), anyInt(), anyString()))
            .thenReturn(Calls.response(expectedWorklistResult))

    mInteractor.getWorklist(mOnWorklistResultListenerMock)

    verify(mOnWorklistResultListenerMock).onWorklistResult(expectedWorklistResult)
    verifyNoMoreInteractions(mOnWorklistResultListenerMock)
}

运行时我不断收到以下消息:

  

想要但未被援引:   onWorklistResultListener.onWorklistResult(          );    - > at com.example.dialogs.worklistdialog.WorklistInteractorTest $当worklistquery返回结果时,通过监听器传递结果OnWorklistResult()$ 1.doResume(WorklistInteractorTest.kt:58)

     

实际上,这个模拟没有互动。

2 个答案:

答案 0 :(得分:3)

您找到了解决方案,但也许对于其他人,尤其是初学者,可能有助于解释为什么您首先会遇到错误以及为什么runBlocking会有所帮助。问题是,在运行单元测试时,期望的是,这些测试是同步的,也就是说,如果任何代码在某个单独的线程上执行一段时间后,测试运行器将永远不会知道,因为对于测试运行器当测试运行程序的线程上的方法调用完成时,测试结束。

因此无法测试未在主线程上运行的代码。事实上,尝试这样做甚至是一种不好的做法。由于无法保证在另一个线程上运行的代码何时完成(如果有的话),我们不知道主线程当时将处于什么状态(主线程可能甚至不再存在,如同情况一样)与单元测试运行器)。

即使在某种程度上可以测试在其他线程中执行的代码,每次测试运行都不可避免地会有所不同(由于上一段的结果),并且测试可能会在每次运行时产生不同的结果。这直接违背了测试可靠的意识形态,并在每次运行中产生相同的结果。

因此,在测试时,所有经过测试的代码都需要在主线程上运行,并且应该确保永远不要尝试在测试中调用任何异步代码。

runBlocking协程,在它启动的线程上(与launch相反)以阻塞的方式运行其中的代码,这将导致代码异步运行。 / p>

所有这一点都有一点需要注意,协程不是线程,但是如果你在thread的上面文本中替换单词asynchronous coroutine,文本就不会失去任何意义。我所说的适用于您使用传统线程时以及使用协同程序时。

答案 1 :(得分:1)

必须大力改变实施。事实证明,使用常规函数中的launch coroutine-builder不会直接向Android活动/片段中使用。

我没有使用常规函数,而是将getWorklist()函数更改为挂起函数并使用了withContext coroutine-builder。这是新的实现:

override suspend fun getWorklist(listener: OnWorklistResultListener) {
        withContext(CommonPool) {
            Timber.i("Loading worklist")
            val result = worklistRepository.getWorklist(
                    ip = preferenceManager.worklistIp,
                    port = preferenceManager.worklistPort,
                    aeTitle = preferenceManager.worklistAeTitle)
                    .awaitResult()
            when (result) {
            //Successful HTTP result
           ... left out for brevity (it's the same as before) ...

现在所有测试都通过了。