Vert.x 3.8.1单元测试出现TimeoutException问题咨询
嘿,我之前也踩过这个Vert.x单元测试的坑!你明明在代码里加了completeNow(),也没有阻塞操作,结果还是触发了超时,确实挺让人困惑的。让我帮你分析下问题,再给出几个解决办法。
你的代码与报错信息
TestMainVerticle.java
@ExtendWith(VertxExtension.class) public class TestMainVerticle { @BeforeEach void deploy_verticle(Vertx vertx, VertxTestContext testContext) { vertx.deployVerticle(new MainVerticle(), testContext.succeeding(id -> testContext.completeNow())); } @Test void verticle_deployed(Vertx vertx, VertxTestContext testContext) throws Throwable { testContext.completeNow(); } }
MainVerticle.java
public class MainVerticle extends AbstractVerticle { @Override public void start(Promise<Void> startPromise) throws Exception { } }
报错输出
测试执行超时。请确保您的异步代码包含对VertxTestContext#completeNow()、VertxTestContext#failNow()或Checkpoint#flag()的调用。
java.util.concurrent.TimeoutException: 测试执行超时。请确保您的异步代码包含对VertxTestContext#completeNow()、VertxTestContext#failNow()或Checkpoint#flag()的调用 at io.vertx.junit5.VertxExtension.joinActiveTestContexts(VertxExtension.java:230) at io.vertx.junit5.VertxExtension.beforeTestExecution(VertxExtension.java:191) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeTestExecutionCallbacks$5(TestMethodTestDescriptor.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:169) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeTestExecutionCallbacks(TestMethodTestDescriptor.java:154) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:125) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) ...
问题根源
问题出在你的@BeforeEach方法里的部署逻辑!因为你的MainVerticle的start方法是同步完成的(空实现,直接就完成了Promise),导致vertx.deployVerticle的成功回调是在当前线程同步执行的——这时候VertxTestContext还没完全初始化好,你提前调用了completeNow(),会让测试上下文的状态出现异常,后续测试执行时就会误以为没有正确标记完成,从而触发超时。
解决办法
1. 使用Checkpoint替代直接调用completeNow()
这是最稳妥的方案,Checkpoint是Vert.x JUnit5扩展专门设计用来同步测试状态的工具,能避免同步回调导致的上下文问题:
@BeforeEach void deploy_verticle(Vertx vertx, VertxTestContext testContext) { // 创建一个检查点 Checkpoint deploymentCheckpoint = testContext.checkpoint(); vertx.deployVerticle(new MainVerticle(), testContext.succeeding(id -> { // 标记检查点完成 deploymentCheckpoint.flag(); })); }
2. 让MainVerticle的start方法异步完成
如果你的业务逻辑本来就是异步的,或者可以模拟异步操作,让start方法通过Vert.x的事件循环来完成Promise,就能避免同步回调的问题:
@Override public void start(Promise<Void> startPromise) throws Exception { // 用一个1ms的定时器模拟异步初始化 vertx.setTimer(1, id -> startPromise.complete()); }
3. 升级Vert.x版本到3.8.5及以上
这个问题在Vert.x 3.8.5的小版本更新里被官方修复了,他们针对同步Verticle部署的测试场景做了优化。如果你的项目允许升级版本,这是最省心的解决方案。
验证
按照上面的任意一种方法修改后,再运行单元测试,应该就能正常通过,不会再出现TimeoutException了!
内容的提问来源于stack exchange,提问作者Jingwei Li

