Logo

dev-resources.site

for different kinds of informations.

How to write beautiful unit tests in Vert.x

Published at
4/24/2020
Categories
sip3
kotlin
vertx
testing
Author
agafox
Categories
4 categories in total
sip3
open
kotlin
open
vertx
open
testing
open
Author
6 person written this
agafox
open
How to write beautiful unit tests in Vert.x

As you already know from my previous blog post the SIP3 team is in love with the incredible Vert.x framework.

Reactive nature makes Vert.x extremely fast and scalable, but (as everything else) it all comes with a price πŸ‘Ώ... And here I'm talking about writing unit tests (😈😈😈 of course if you do write them 😈😈😈)...

Is it really that complicated to write tests with Vert.x?

Let's assume that we want to check a simple thing: that a message sent via event bus was received by its consumer.

In a perfect world we would expect to see something like this (please pay attention to the test structure):

class MyVertxTest {

    @Test
    fun `Send message to the address`() {
        // 1. Define our message
        val message = "Hello, world!"

        val vertx = Vertx.vertx()

        // 2. Send it to the address
        vertx.eventBus().send("address", message)
        // 3. Retrieve and check the message
        vertx.eventBus().consumer<String>("address") { event ->
            assertEquals(message, event.body())
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Obviously this code won't be working because:

  1. There is no consumer at the moment we've sent the message;
  2. Even if there was a consumer assertEquals() will be executed not in the context of our test thread.

Of course, the problem is not new and Vert.x team has already resolved it by introducing a TestContext object (learn more about that from official documentation: here and here).

With the TestContext and JUnit5 our code will look like this:

@ExtendWith(VertxExtension::class)
class MyVertxTest {

    @Test
    fun `Send message to the address`() {
        // 1. Define our message
        val message = "Hello, world!"

        val context = VertxTestContext()
        val vertx = Vertx.vertx()

        // 3. Retrieve and check the message
        vertx.eventBus().consumer<String>("address") { event ->
            context.verify {
                assertEquals(message, event.body())
            }
            context.completeNow()
        }

        // 2. Send it to the address
        vertx.eventBus().send("address", message)

        assertTrue(context.awaitCompletion(5, TimeUnit.SECONDS))
        if (context.failed()) {
            throw context.causeOfFailure();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Not like in our previous example, this code will be working fine, but:

  1. Assertion on the context object brings too much overhead;
  2. We would love to keep the order - define test context, execute scenario and only after make all the assertions.

Trying to satisfy the last two requirements the SIP3 team decided to introduce its own test class wrapper:

@ExtendWith(VertxExtension::class)
open class VertxTest {

    lateinit var context: VertxTestContext
    lateinit var vertx: Vertx

    fun runTest(deploy: (suspend () -> Unit)? = null, execute: (suspend () -> Unit)? = null,
                assert: (suspend () -> Unit)? = null, cleanup: (() -> Unit)? = null, timeout: Long = 10) {
        context = VertxTestContext()
        vertx = Vertx.vertx()
        GlobalScope.launch(vertx.dispatcher()) {
            assert?.invoke()
            deploy?.invoke()
            execute?.invoke()
        }
        assertTrue(context.awaitCompletion(timeout, TimeUnit.SECONDS))
        cleanup?.invoke()
        if (context.failed()) {
            throw context.causeOfFailure()
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

We put a context object assertion within the runTest() method. Also we used Kotlin named arguments(which is a really cool) to define test stages.

Let's rewrite our test class using VertxTest:

class MyVertxTest : VertxTest() {

    @Test
    fun `Send message to the address`() {
        // 1. Define our message
        val message = "Hello, world!"

        runTest(
                execute = {
                    // 2. Send it to the address
                    vertx.eventBus().send("address", message)
                },
                assert = {
                    // 3. Retrieve and check the message
                    vertx.eventBus().consumer<String>("address") { event ->
                        context.verify {
                            assertEquals(message, event.body())
                        }
                        context.completeNow()
                    }
                }
        )
    }
}
Enter fullscreen mode Exit fullscreen mode

Of course, it's a matter of taste but we think that the code above is clean and simple even though there is always room for improvement πŸ˜„.

The SIP3 team uses this approach in all it projects. Feel free to check out our github and let us know if you loved it.

Cheers...

vertx Article's
28 articles in total
Favicon
Error handlers and failure handlers in Vert.x
Favicon
Why we discarded Reactive systems architecture from our code?
Favicon
Build web application in Vert.x [Part 1/ ♾️]
Favicon
Yet another ode to Vert.x, or how to write a performance-wise expiring map in less than 100 lines of code.
Favicon
Surprising Qualities of Event Driven System
Favicon
Idiomatic Kotlin Abstractions for the Vert.x EventBus
Favicon
Vert.x Circuit Breaker
Favicon
Writing Async Tests for Vert.x using Kotlin
Favicon
Reducing Boilerplate in Vert.x Tests written in Kotlin
Favicon
Writing Vert.x Integration Tests with Kotlin & Testcontainers
Favicon
Quarkus: Entendendo a relação entre o Mutiny e o Vert.x
Favicon
HTTPS Client Certificate Authentication With Java
Favicon
Throttle HTTP requests on paged resources with Vert.x
Favicon
Supercharge Your Kotlin Vert.x Application with EventBus Extensions
Favicon
Handle backpressure between Kafka and a database with Vert.x
Favicon
Handling unknown JSON structures
Favicon
Introduction to Vert.x
Favicon
Future Composition in Vert.x
Favicon
How to extend Vert.x EventBus API to save on serialization.
Favicon
How to write beautiful unit tests in Vert.x
Favicon
Scaling Vert.x application for session dependent data processing.
Favicon
KVision v3.7.0 is released (with Vert.x support)
Favicon
Reactive Java using the Vert.x toolkit
Favicon
Vert.x Kotlin Coroutines
Favicon
How we built a RESTful API with Vert.x, Kotlin Coroutines and Keycloak
Favicon
vertx-jooq 2.4 released
Favicon
Sirix - Released 0.9.1 (time travel queries and versioning made easy)
Favicon
Reactive Programming with Kotlin - Quick Intro to Vert.x

Featured ones: