我想使用 mockK 库为下面的函数编写单元测试:
void function_name() {
new Thread(() -> {
try {
Thread.sleep(4000);
//some code
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
我尝试使用模拟中提供的线程:
@Test
fun function_name() {
val bundle = spyk()
Thread {
Thread.sleep(4000)
presenter.navigateToScreen(bundle)
}.start()
verify(timeout = 5000){
//to verify statement
}
}
但测试无法验证验证块中提供的验证。
如何测试上述包含延迟的功能。
您的示例还不够完整,无法清楚地说明您要做什么,但似乎您的测试:
- 启动休眠 4 秒的线程,然后调用
presenter.navigateToScreen()
。 - 当该线程仍处于休眠状态时,您有一个验证,允许 5 秒满足某些条件
- 在验证期间,第一个线程将完成其 4 秒的睡眠时间(剩余 1 秒的验证时间(并调用可能调用
function_call()
方法的presenter.navigateToScreen()
。 - 你的
function_call()
方法——最多剩下 1 秒verify{}
——会休眠 4 秒,耗尽verify{}
的耐心,然后跑// Some code
。 - 由于
verify{}
可能正在测试在// Some code
中完成的内容,该内容在verify{}
完成后运行3秒,因此它永远不会验证。
因此,您希望从测试代码中删除睡眠。如果你的意图是确保调用 sleep(4000(,请考虑模拟它,正如我在下面提到的。
笔记:
当我测试线程- 时(如果可以的话(,我将线程执行的代码移动到不同的方法或类中,这样我就可以独立于线程处理来测试逻辑。
- 当我测试诸如
Thread.sleep()
(或Instant.now()
(之类的东西时,我会注入方法并模拟它们或根据需要验证它们。这意味着测试根本不需要等待任何实时时间:被测试的代码只需要相信被嘲笑的sleep()
或now()
合谋让他们相信的任何内容。
(我也认为将辅助全局的东西称为"timeMachine"和"sleepMachine"很有趣,尽管它们实际上是工厂,我想:它可以多次节省打字{ millis -> Thread.sleep(millis) }
。
阻止引用,因为代码的东西不喜欢我的 Kotlin:
fun trySeveral( tries: Int, sleepMs: (Long) -> Unit = { millis -> Thread.sleep(millis) }, now: () -> Instant = { Instant.now() } ): Duration? { repeat(tries) { try { val started = now() // some code that throws or doesn't throw return Duration.between(started, now()) } catch (e: InterruptedException) { logger("Failed attempt $it ", e) } sleepMs(200) // Some logic to avoid final sleep } return null }
在这里,我可以通过模拟now()
和/或验证sleep()
来测试函数,具体取决于上面注释掉的部分是否抛出:
val sleepMs = mockk<(Long) -> Unit>(relaxed = true) val now = mockk<() -> Instant>() every { now() } returnsMany listOf(1,2,3).map { Instant.ofEpochSecond(it) } trySeveral(3, sleepMs, now) verify(exactly = 2) { // TODO: skip the final sleep sleepMs(200) }
你也可以嘲笑静态,但我不喜欢这样,因为<喃喃自语全球范围喃喃自语>:喃喃自语全球范围喃喃自语>
mockkStatic(Thread::class) every { Thread.sleep(any()) } just Runs