從 runBlockingTest 到 runTest#
在 kotlinx-coroutines-test 早期版本中,最常見的測試入口是 runBlockingTest。它的功能是:
- 建立一個會「跳過 delay」的 coroutine scope。
- 在裡面可以呼叫 suspend 函式進行測試。
- 預設使用會自動加速時間的 dispatcher。
但 runBlockingTest 後來被標記為 deprecated,原因包含:
- 它對於「未完成的 coroutine」的判定過於嚴格,常常需要 hack 才能寫好測試。
- 與
Job、SupervisorJob等 scope 行為的整合不夠乾淨。 - 與
TestDispatcher的設計不一致。
新版 API 改採 runTest,搭配 TestScope,行為更直觀。
runBlockingTest 範例(舊版)#
直接看一個 1500 毫秒會超時的測試:
@OptIn(ExperimentalCoroutinesApi::class)
@Test
internal fun downloadTest_Fail() = runBlockingTest {
var isException = false
try {
day27.download(FakeService(1500))
} catch (e: TimeoutCancellationException) {
isException = true
}
assert(isException)
}原本要等 1500 毫秒的 delay,在 runBlockingTest 中只花了大約 50 毫秒。時間被加速了 — 這就是測試函式庫最有價值的地方。
runTest 的基本用法#
新版的等價寫法:
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun downloadTest() = runTest {
val day27 = Day27()
assertEquals("done", day27.download(FakeService(100)))
}語法幾乎一致,但 runTest:
- 回傳的是
TestResult(在 JVM 上等價於 Unit,在 JS 上是 Promise)。 - 使用
TestScope作為 coroutine scope。 - 支援更細緻的時間控制,搭配 TestCoroutineScheduler 的 advanceTimeBy、advanceUntilIdle、currentTime。
runTest 的時間語意#
在 runTest 區塊中:
delay(1000)不會真的等 1 秒,而是讓「虛擬時鐘」前進 1 秒。- 區塊預設會在所有 coroutine 完成後才結束。
- 可以透過 scheduler 控制時間前進的時機。
例如:
@Test
fun timeAdvance() = runTest {
val start = currentTime
delay(2000)
val end = currentTime
assertEquals(2000L, end - start)
}這裡的 currentTime 是 TestCoroutineScheduler 維護的虛擬時間,並非真實時間。
與 runBlocking 的差異#
| 項目 | runBlocking | runTest |
|---|---|---|
| 適用場景 | 一般程式進入點 | 測試專用 |
delay 行為 | 真的等待 | 虛擬時間,立即前進 |
| Scope 型別 | CoroutineScope | TestScope |
| 取消行為 | 一般 | 與 TestDispatcher 整合 |
不要在測試裡用 runBlocking 包 suspend 函式來測時間相關邏輯,否則 CI 會被拖很慢。
從 runBlockingTest 遷移到 runTest 的要點#
- 將
runBlockingTest { ... }換成runTest { ... }。 - 若測試需要明確的 dispatcher,改用 StandardTestDispatcher 或 UnconfinedTestDispatcher 配合 runTest。
- 取得 scheduler 的方式從
coroutineContext[TestCoroutineExceptionHandler]改為testScheduler。 - 直接使用
advanceTimeBy、advanceUntilIdle來控制時間流動。
小結#
- runTest 是新版測試入口,取代被棄用的 runBlockingTest。
- runTest 內部使用 TestScope 與 TestDispatcher,提供虛擬時間控制。
- 寫測試時優先選用 runTest,避免時間相關邏輯拖慢測試。
原文出處#
- 原書/iThome:https://ithelp.ithome.com.tw/articles/10276586