如何使用 mockK 为线程编写单元测试



我想使用 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
}
}

但测试无法验证验证块中提供的验证。

如何测试上述包含延迟的功能。

您的示例还不够完整,无法清楚地说明您要做什么,但似乎您的测试:

  1. 启动休眠 4 秒的线程,然后调用presenter.navigateToScreen()
  2. 当该线程仍处于休眠状态时,您有一个验证,允许 5 秒满足某些条件
  3. 在验证期间,第一个线程将完成其 4 秒的睡眠时间(剩余 1 秒的验证时间(并调用可能调用function_call()方法的presenter.navigateToScreen()
  4. 你的function_call()方法——最多剩下 1 秒verify{}——会休眠 4 秒,耗尽verify{}的耐心,然后跑// Some code
  5. 由于verify{}可能正在测试在// Some code中完成的内容,该内容在verify{}完成后运行3秒,因此它永远不会验证。

因此,您希望从测试代码中删除睡眠。如果你的意图是确保调用 sleep(4000(,请考虑模拟它,正如我在下面提到的。

笔记:

当我测试线程
  1. 时(如果可以的话(,我将线程执行的代码移动到不同的方法或类中,这样我就可以独立于线程处理来测试逻辑。
  2. 当我测试诸如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

最新更新