eclipse-vertx / vertx-junit5

Testing Vert.x applications with JUnit 5
Apache License 2.0
44 stars 32 forks source link

RunTestOnContext prematurely kills Vertx before AfterAll complete #116

Closed thced closed 2 years ago

thced commented 2 years ago

Version

4.2.6

Context

While executing consecutive async queries inside an @AfterAll method, the calls fail to complete due to pool (PgPool) is already closed.

Do you have a reproducer?

No, but a simple JUnit test can trigger the same issue. The testContext is never called inside the timer, because vertx has been shutdown inside RunTestOnContext via interceptAfterAllMethod

import io.vertx.junit5.RunTestOnContext;
import io.vertx.junit5.VertxExtension;
import io.vertx.junit5.VertxTestContext;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
​
@ExtendWith(VertxExtension.class)
class HookTest {
​
  @RegisterExtension
  static RunTestOnContext runTestOnContext = new RunTestOnContext();
​
  @AfterAll
  static void afterAll(VertxTestContext testContext) {
    var vertx = runTestOnContext.vertx();
    vertx.setTimer(50L, id -> testContext.completeNow());
  }
​
  @Test
  void testThat(VertxTestContext testContext) {
    Assertions.assertTrue(true);
    testContext.completeNow();
  }
}

The "solution" seems to be to delay the shutdown of Vert.x, like so:

@ExtendWith(VertxExtension.class)
class HookTest {

  private static final Promise<Void> latchToFinishTestAndCloseVertx = Promise.promise();

  //@RegisterExtension static RunTestOnContext runTestOnContext = new RunTestOnContext();
  @RegisterExtension static RunTestOnContext runTestOnContext =
      new RunTestOnContext(
          () -> Future.succeededFuture(Vertx.vertx()),
          vertx -> latchToFinishTestAndCloseVertx.future().compose(v -> vertx.close()));

  @AfterAll
  static void afterAll(VertxTestContext testContext) {
    var vertx = runTestOnContext.vertx();
    vertx.setTimer(50L, id -> {
      latchToFinishTestAndCloseVertx.complete();
      testContext.completeNow();
    });
  }

  @Test
  void testThat(VertxTestContext testContext) {
    Assertions.assertTrue(true);
    testContext.completeNow();
  }
}

Extra

jponge commented 2 years ago

Thanks for your report.

Your afterAll method shall wait for context completion, as in:

testContext.awaitCompletion(1, TimeUnit.SECONDS);

This is because you access a Vertx object from the RunOnContext extension, not from a classic parameter injection.

This should fix the problem you are facing, feel-free to re-open the issue if that's not the case 👍

thced commented 2 years ago

Hi, I don't think that is the solution @jponge ..

I have been trying a bit, and the afterAll method in test is run by the eventloop thread, and the thread that calls "cleanUp()" (afterAll()) in RunTestOnContext is the "main" thread. Calling testContext.awaitCompletion() cannot possibly help since the method is not executed by the main thread.

import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.junit5.*;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.*;

@ExtendWith(VertxExtension.class)
class DummyTest {

  @RegisterExtension static RunTestOnContext rtoc = new RunTestOnContext();

  @Test
  void someTest(VertxTestContext testContext) {
    testContext.completeNow();
  }

  @AfterAll
  static void afterAll(VertxTestContext testContext) {
    Vertx vertx = rtoc.vertx();
    Future.future(p -> vertx.setTimer(50, p::complete))
        .onComplete(testContext.succeedingThenComplete());
  }
}