wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Async testing with vert.x

Hero image for 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 on 03 November 2022 | Comments (0) | categories: Java vert.x

Comments

  1. No comments yet, be the first to comment