Async testing with vert.x
Test driven development gets interesting when actions might or might not complete somewhen in the future. Point in case: HTTP requests (fetch returns a promise)
vert.x and jUnit5
I frequently write little tests, that spin up an http sever before the test, run individual tests using a WebClient. All these operations run asynchronous, returning a vert.x Future (In JavaScript Land the equivalent would be a promise)
To make this pain free, vert.x provides a full integration into jUnit 5. Using the annotation @ExtendWith(VertxExtension.class)
vert.x provides two injectable parameters: Vertx and VertxTestContext. Add the simple mental rule: All async operations need to interact with the VertxTestContext.
Sample test to get started
This is a simple test skeleton, loading an http server for every request. Most likely in a real test you would spin-up the server once using @BeforeAll
instead of @BeforeEach
. Matching the @BeforeEach
you will need an @AfterEach
to wind down the server. Fun part: it apparently work without, until it doesn't. Welcome to the asynchronous world. So save yourself the head scratching and prepare both methods.
@BeforeEach
void beforeEach(final Vertx vertx, final VertxTestContext testContext) throws Exception {
final Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.route().handler(this::echo);
vertx.createHttpServer()
.requestHandler(router)
.listen(thePort)
.onFailure(testContext::failNow)
.onSuccess(h -> {
this.server = h;
testContext.completeNow();
});
}
@AfterEach
void afterEach(final Vertx vertx, final VertxTestContext testContext) throws Exception {
this.server.close()
.onFailure(testContext::failNow)
.onSuccess(v -> testContext.completeNow());
}
To have a little more fun, my test uses a @ParameterizedTest
that allows to run a test multiple times with different inputs. I used the @MethodSource
annotation that calls this method:
static Stream<Arguments> testCases() {
return Stream.of(
Arguments.of("color", "red"),
Arguments.of("dance", "tango"),
Arguments.of("food", "noodles"),
Arguments.of("sky", "blue"),
Arguments.of("happy", "ness"));
}
To successfully run the paramterized Test, the @MethodSource
provided parameters preceed the Vertx and VertxTestContext. So your test method looks like this:
@ParameterizedTest
@MethodSource("testCases")
void test2(final String key, final String value, final Vertx vertx,
final VertxTestContext testContext) {
final WebClient client = WebClient.create(vertx);
final JsonObject body = new JsonObject().put(key, value);
client.post(thePort, "localhost", "/")
.putHeader("ContentType", "application/json")
.sendJson(body)
.onFailure(testContext::failNow)
.onSuccess(result -> {
testContext.verify(() -> {
Assertions.assertEquals(200, result.statusCode());
Assertions.assertEquals(body, result.bodyAsJsonObject());
});
testContext.completeNow();
});
}
There's more to async testing, so read the documentation. The sample class can be found here.
As usual YMMV
Posted by Stephan H Wissel on 03 November 2022 | Comments (0) | categories: Java vert.x