我有一个可挂起的函数,我想断言,在某些情况下,它不会以结果来完成.我try 编写以下扩展,它的目标是在断言作业(job)是否完成之前等待5秒(我认为这个判断足以知道挂起的对象仍处于挂起状态):

First Approach:

// Extension Function
fun <T> TestScope.assertNotCompleted(block: suspend CoroutineScope.() -> T) {
    val result = async { block }
    advanceTimeBy(5000L)
    val isBlockComplete = result.isCompleted
    assertThat(isBlockComplete, equalTo(false))
}

// Usage
@Test
fun `Given no value is ready, when waitForValue is called, then the suspendable function is not complete`() =
    runTest {
        assertNotCompleted {
            someClass.waitForValue() 
        }
    }

然而,在这种情况下,当result.isCompleted应该为FALSE时,它总是返回TRUE.如果我go 掉advanceTimeBy(5000L),那么result.isCompleted总是返回false,即使我修改测试以实际返回一些东西.

我试过另一种方法,用getCompletionExceptionOrNull()投出IllegalStateException分.这实际上是有效的,但它导致了一个奇怪的接口,在该接口中,我们需要使用"Expect"属性来注释使用它的每个测试.如果可能的话,我想避免这种情况,因为测试中的其他地方可能会抛出异常,因此测试可能会不正确地通过.

Second Approach:

// Extension Function
@OptIn(ExperimentalCoroutinesApi::class)
fun <T> TestScope.assertNotCompleted(block: suspend CoroutineScope.() -> T) {
    async(block = block).run {
        this@assertNotCompleted.advanceTimeBy(5000L)
        getCompletionExceptionOrNull()
    }
}

// Usage - wanting to avoid the need for expected property
@Test(expected = IllegalStateException::class)
fun `Given no value is ready, when waitForValue is called, then the suspendable function is not complete`() =
    runTest {
        assertNotCompleted {
            someClass.waitForValue() 
        }
    }

我确实try 过捕捉这个异常,但是测试要么总是通过,要么总是失败,这取决于advanceTimeBy(5000L)的位置--这与可挂起的对象能否完成无关.

Thrid Approach:

// Extension Function
@OptIn(ExperimentalCoroutinesApi::class)
fun <T> TestScope.assertNotCompleted(block: suspend CoroutineScope.() -> T) {
    runCatching {
        val result = async { 
            block
            // advanceTimeBy(5000L) Test ALWAYS passes
        }
        // advanceTimeBy(5000L) Test ALWAYS fails
        result.getCompletionExceptionOrNull()
    }.also { 
        assertThat(it.isFailure, equalTo(true))
    }
}

// Usage is same as first approach

先谢谢你.

推荐答案

给定一个总是完成的函数和一个永不完成的函数

suspend fun alwaysCompletes(): Int {
    delay(100)
    return 42
}

suspend fun neverCompletes() {
    while(true) {
        delay(1000)
    }
}

这些测试通过:

@Test
fun testAlwaysCompletes() {
    runBlocking {
        val job = launch {
            someClass.alwaysCompletes()
        }
        delay(5000) // wait for 5 seconds
        assertThat(true, equalTo(job.isCompleted ))
    }
}

@Test
fun testNeverCompletes() {
    runBlocking {
        val job = launch {
            someClass.neverCompletes()
        }
        delay(5000) // wait for 5 seconds
        assertThat(false, equalTo(job.isCompleted ))
        job.cancelAndJoin() // cancel the job to avoid leaking resources
    }
}


或提前模拟时间TimeBy:



@Test
fun testAlwaysCompletes() = runTest {
    val job = launch {
        someClass.alwaysCompletes()
    }

    advanceTimeBy(10_000) // advance time by 10,000ms
    assertFalse(job.isActive) // job should not be active
    assertTrue(job.isCompleted) // job should now be completed

}

@Test
fun testNeverCompletes() = runTest {
    val job = launch {
        someClass.neverCompletes()
    }

    advanceTimeBy(10_000) // advance time by 10,000ms
    assertTrue(job.isActive) // job should still be active

    job.cancelAndJoin() // cancel the job to avoid leaking resources
    assertFalse(job.isActive) // job should now be cancelled and not active
}

要验证这些测试断言挂起功能是否已完成,您可以切换功能,两个测试都将失败

Android相关问答推荐

编写视觉转型

在Android应用程序上使用Room数据库时,是否可以预先填充数据

NativeScript在`ns run android`上重复Kotlin类

无法将Kotlin序列化添加到Android项目

Play Google上发布的一款应用的房间数据库迁移

如何解决Gradle构建错误:java.lang.NoSuchMethodError

如何在我的sqlite数据库中获取某个玩家的分数

Android Studio Relay插件(版本0.3.07)错误

错误:无法安装应用程序:INSTALL_PARSE_FAILED_MANIFEST_MALFORMED (React-Native-Android-Studio)

为一组闪烁的可组合项制作动画,控制同步/定时

在 MVVM Jetpack Compose 上添加依赖项时出现重复类错误

Kotlin 协程、 retrofit 、android

Jetpack Compose 惰性列在单个项目更新时重新组合所有项目

找不到(包名称).在以下位置搜索:

根据 Jetpack Compose 中的生命周期正确处理变量/函数

如何使用文件提供程序将视频从一个应用程序共享到另一个应用程序?

如果我在网络请求中指定它们是否与判断网络功能相关

ObjectBox,如何在冲突中放弃一切迁移?

compose :为什么以记住启动的列表触发方式与快照不同

Google API:java.lang.ClassNotFoundException:找不到类sun.misc.Service