【问题标题】:How to write unit test for Threads with mockK如何使用 mockK 为线程编写单元测试
【发布时间】:2020-01-24 10:59:40
【问题描述】:

我想使用 mockK 库为以下函数编写单元测试:

void function_name() {
    new Thread(() -> {
        try {
            Thread.sleep(4000);
            //some code
        } catch (InterruptedException e) {
              e.printStackTrace();
        }
    }).start();
}

我尝试使用 mockk 中提供的 Thread:

@Test
fun function_name() {
    val bundle = spyk()

    Thread {
        Thread.sleep(4000)
        presenter.navigateToScreen(bundle)
    }.start()

    verify(timeout = 5000){
        //to verify statement
    }
}

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

如何测试上述包含延迟的函数。

【问题讨论】:

    标签: android unit-testing mockk


    【解决方案1】:

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

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

    因此,您想从测试代码中删除 Sleep。如果您的意图是确保调用 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
    

    【讨论】:

      猜你喜欢
      • 2011-03-16
      • 1970-01-01
      • 1970-01-01
      • 2023-01-28
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多