CompletableFuture 运行 时在 Spock 的 Mock 等待中睡眠
Sleep in Spock's Mock waits when run by CompletableFuture
当我单独 运行 runAsyncWithMock
测试时,它会等待 3 秒直到模拟的执行完成,而不是像其他 2 个测试那样被终止。
我不明白为什么。
有趣的是:
- 在
runAsyncWithMock
测试中,当CompletableFuture.runAsync
连续执行多个Runnables
时,只有第一个等待,其他的不等待。
- 当有多个重复的
runAsyncWithMock
测试时,当整个规范被执行时,每个测试 运行s 持续 3s。
- 当使用 Class 实例而不是 Mock 时,测试会立即完成。
知道我错了什么吗?
我的配置:
- macOS 莫哈韦沙漠 10.14.6
- 斯波克 1.3-groovy-2.4
- Groovy 2.4.15
- JDK1.8.0_201
包含整个 Gradle 复制项目的回购:
https://github.com/lobodpav/CompletableFutureMisbehavingTestInSpock
有问题的测试代码:
@Stepwise
class SpockCompletableFutureTest extends Specification {
def runnable = Stub(Runnable) {
run() >> {
println "${Date.newInstance()} BEGIN1 in thread ${Thread.currentThread()}"
sleep(3000)
println "${Date.newInstance()} END1 in thread ${Thread.currentThread()}"
}
}
def "runAsyncWithMock"() {
when:
CompletableFuture.runAsync(runnable)
then:
true
}
def "runAsyncWithMockAndClosure"() {
when:
CompletableFuture.runAsync({ runnable.run() })
then:
true
}
def "runAsyncWithClass"() {
when:
CompletableFuture.runAsync(new Runnable() {
void run() {
println "${Date.newInstance()} BEGIN2 in thread ${Thread.currentThread()}"
sleep(3000)
println "${Date.newInstance()} END2 in thread ${Thread.currentThread()}"
}
})
then:
true
}
}
哦,刚刚写完我的最后一条评论,我看到了不同之处:
您使用 @Stepwise
(我一开始尝试时没有),这是一个我几乎从不使用的注解,因为它会在特征方法之间创建依赖关系(糟糕、糟糕的测试实践)。虽然我不能说为什么这只有在 运行 使用第一种方法时才会产生你描述的效果,但我可以告诉你删除注释可以修复它。
P.S.: 使用 @Stepwise
你甚至不能单独执行第二种或第三种方法,因为 运行ner 总是先 运行 前面的方法,因为 - 好吧,据说规范是逐步执行的。 ;-)
更新: 我可以用 @Stepwise
简单地重现这个问题,但是在重新编译之后它不再发生了,不管有没有那个注释。
这是由 https://github.com/spockframework/spock/blob/master/spock-core/src/main/java/org/spockframework/mock/runtime/MockController.java 中的 synchronized
方法引起的,当执行模拟时,它通过 handle
方法委托。该规范还使用 synchronized
方法,在本例中可能是 leaveScope
,因此被休眠存根方法阻塞。
由于这是一个线程交错问题,我猜 runAsyncWithMockAndClosure
中的附加闭包将存根方法的执行移到了 leaveScope
后面,从而改变了 ordering/blocking.
当我单独 运行 runAsyncWithMock
测试时,它会等待 3 秒直到模拟的执行完成,而不是像其他 2 个测试那样被终止。
我不明白为什么。
有趣的是:
- 在
runAsyncWithMock
测试中,当CompletableFuture.runAsync
连续执行多个Runnables
时,只有第一个等待,其他的不等待。 - 当有多个重复的
runAsyncWithMock
测试时,当整个规范被执行时,每个测试 运行s 持续 3s。 - 当使用 Class 实例而不是 Mock 时,测试会立即完成。
知道我错了什么吗?
我的配置:
- macOS 莫哈韦沙漠 10.14.6
- 斯波克 1.3-groovy-2.4
- Groovy 2.4.15
- JDK1.8.0_201
包含整个 Gradle 复制项目的回购:
https://github.com/lobodpav/CompletableFutureMisbehavingTestInSpock
有问题的测试代码:
@Stepwise
class SpockCompletableFutureTest extends Specification {
def runnable = Stub(Runnable) {
run() >> {
println "${Date.newInstance()} BEGIN1 in thread ${Thread.currentThread()}"
sleep(3000)
println "${Date.newInstance()} END1 in thread ${Thread.currentThread()}"
}
}
def "runAsyncWithMock"() {
when:
CompletableFuture.runAsync(runnable)
then:
true
}
def "runAsyncWithMockAndClosure"() {
when:
CompletableFuture.runAsync({ runnable.run() })
then:
true
}
def "runAsyncWithClass"() {
when:
CompletableFuture.runAsync(new Runnable() {
void run() {
println "${Date.newInstance()} BEGIN2 in thread ${Thread.currentThread()}"
sleep(3000)
println "${Date.newInstance()} END2 in thread ${Thread.currentThread()}"
}
})
then:
true
}
}
哦,刚刚写完我的最后一条评论,我看到了不同之处:
您使用 @Stepwise
(我一开始尝试时没有),这是一个我几乎从不使用的注解,因为它会在特征方法之间创建依赖关系(糟糕、糟糕的测试实践)。虽然我不能说为什么这只有在 运行 使用第一种方法时才会产生你描述的效果,但我可以告诉你删除注释可以修复它。
P.S.: 使用 @Stepwise
你甚至不能单独执行第二种或第三种方法,因为 运行ner 总是先 运行 前面的方法,因为 - 好吧,据说规范是逐步执行的。 ;-)
更新: 我可以用 @Stepwise
简单地重现这个问题,但是在重新编译之后它不再发生了,不管有没有那个注释。
这是由 https://github.com/spockframework/spock/blob/master/spock-core/src/main/java/org/spockframework/mock/runtime/MockController.java 中的 synchronized
方法引起的,当执行模拟时,它通过 handle
方法委托。该规范还使用 synchronized
方法,在本例中可能是 leaveScope
,因此被休眠存根方法阻塞。
由于这是一个线程交错问题,我猜 runAsyncWithMockAndClosure
中的附加闭包将存根方法的执行移到了 leaveScope
后面,从而改变了 ordering/blocking.