unit testing使用kotlin协同程序的翻新调用

我正在使用kotlin协程和kotlin改进协程在我正在进行的项目中执行网络请求。 但我无法弄清楚如何让我的unit testing通过逻辑。

这是我的代码:

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

这是我的unit testing之一:

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

事实上,这个模拟与零交互。

你find了解决办法,但也许对于其他人,特别是初学者,解释你为什么首先得到错误,以及为什么runBlocking帮助可能是有帮助的。 问题是,运行unit testing时,期望的是这些测试是同步的,也就是说,如果任何代码在一段时间后在某个单独的线程上执行,测试运行者将永远不会知道这一点,因为对于测试运行者当测试运行程序的线程完成时,测试完成。

因此,不可能测试主线程上没有运行的代码。 事实上,尝试这样做甚至是一个糟糕的做法。 由于不能保证在另一个线程上运行的代码何时完成(如果有的话),我们不知道当前主线程将处于什么状态(主线程可能不再存在,情况也是如此)与unit testing亚军)。

即使在某种程度上可以测试在其他线程中执行的代码,每个测试运行都不可避免地会有所不同(作为上述的结果),并且测试可能在每次运行中产生不同的结果。 这直接违背了可靠的测试思想,并在每次运行中产生相同的结果。

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

runBlocking协同程序在启动线程(而不是launch ,这将导致代码异步运行)中以阻塞的方式运行代码。

有一点需要注意的是,协程并不是线程,但是如果将上述文本中的字thread替换为asynchronous coroutine ,则文本不会失去任何意义。 我说的是适用于当你使用传统的线程,也是当你使用协程。

必须相当大地改变执行。 事实certificate,从一个常规函数中使用launch协程生成器并不是从一个Android Activity / Fragment中直接使用的。

我没有使用常规函数,而是将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) ... 

现在所有的测试都通过了。