wissel.net

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

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