[英]Android Kotlin - Unit test a delayed coroutine that invokes a lambda action when delay is finished
我已经为此苦苦挣扎了很长一段时间,也许有人可以提供帮助......我在测试中的 class 中有这个 function:
fun launchForegroundTimer(context: Context) {
helper.log("AppRate", "[$TAG] Launching foreground count down [10 seconds]")
timerJob = helper.launchActionInMillisWithBundle(Dispatchers.Main, TimeUnit.SECOND.toMillis(10), context, this::showGoodPopupIfAllowed)
}
So in that function, I first write to some log and then I call a coroutine function that expects a Dispatcher param, how long to wait before running the action, Any object that I would like to pass on to the action and the actual action function时间过去后调用。
所以在这种情况下, this::showGoodPopupIfAllowed
是 class 中的私有方法,在 10,000 毫秒过去后被调用。 这是function:
private fun showGoodPopupIfAllowed(context: Context?) {
if (isAllowedToShowAppRate()) {
showGoodPopup(context)
}
}
在第一个 if 中,在我调用showGoodPopup(context)
之前会发生一堆检查
现在,这里是helper.launchActionInMillisWithBundle
function:
fun <T> launchActionInMillisWithBundle(dispatcher: CoroutineContext, inMillis: Long, bundle: T, action: (T) -> Unit): Job = CoroutineScope(dispatcher).launchInMillisWithBundle(inMillis, bundle, action)
这是实际的 CoroutineScope 扩展 function:
fun <T> CoroutineScope.launchInMillisWithBundle(inMillisFromNow: Long, bundle: T, action: (T) -> Unit) = this.launch {
delay(inMillisFromNow)
action(bundle)
}
What I am trying to achieve is a UnitTest that calls the launchForegroundTimer
function, calls the helper function with the appropriate arguments and also continue through and call that lambda showGoodPopupIfAllowed
function where I can also provide mocked behaviour to all the IF statments that occur in isAllowedToShowAppRate
.
目前,我的测试在调用launchActionInMillisWithBundle
后立即停止并且测试刚刚结束。 我假设没有对任何协程的真正调用,因为我是 mocking helper
class ......不知道如何在这里继续。
我读了一些有趣的文章,但似乎没有一篇能解决这样的 state。 我当前的测试 function 看起来像这样:
private val appRaterManagerHelperMock = mockkClass(AppRaterManagerHelper::class)
private val timerJobMock = mockkClass(Job::class)
private val contextMock = mockkClass(Context::class)
@Test
fun `launch foreground timer`() {
every { appRaterManagerHelperMock.launchActionInMillisWithBundle(Dispatchers.Main, TimeUnit.SECOND.toMillis(10), contextMock, any()) } returns timerJobMock
val appRaterManager = AppRaterManager(appRaterManagerHelperMock)
appRaterManager.launchForegroundTimer(contextMock)
verify(exactly = 1) { appRaterManagerHelperMock.log("AppRate", "[AppRaterManager] Launching foreground count down [10 seconds]") }
}
我使用 mockk 作为我的 Mocking 库。 AppRaterManager
是被测的class
我还想提一下,理论上我可以将协程调用移到正在测试的 class 之外。 因此,像activity.onResume()
这样的外部 class 可以启动某种倒计时,然后直接调用 function 来检查showGoodPopupIfAllowed()
。 但是目前,请假设我没有任何方法可以更改调用代码,因此计时器和协程应该保留在测试域中的 class 中。
谢谢!
好吧,我在https://mockk.io/#capturing上更深入地了解了捕获/答案,发现有一个capture
function。 So I captured the lambda function in a slot
which enables me invoke the lambda and then the actual code continues in the class under test. 我可以从那里模拟行为的 rest。
这是我针对这种情况的测试 function (适用于任何被卡住的人):
@Test
fun `launch foreground timer, not participating, not showing good popup`() {
val slot = slot<(Context) -> Unit>()
every { appRaterManagerHelperMock.launchActionInMillisWithBundle(Dispatchers.Main, TimeUnit.SECOND.toMillis(10), contextMock, capture(slot)) } answers {
slot.captured.invoke(contextMock)
timerJobMock
}
every { appRaterManagerHelperMock.isParticipating() } returns false
val appRaterManager = AppRaterManager(appRaterManagerHelperMock)
appRaterManager.launchForegroundTimer(contextMock)
verify(exactly = 1) { appRaterManagerHelperMock.log("AppRate", "[AppRaterManager] Launching foreground count down [10 seconds]") }
verify(exactly = 1) { appRaterManagerHelperMock.isParticipating() }
verify(exactly = 0) { appRaterManagerHelperMock.showGoodPopup(contextMock, appRaterManager) }
}
所以现在剩下的就是如何测试协程在提供的延迟时间结束后实际调用 lambda。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.