TDD in an event driven application
Tags: eventdriven, async, tdd, kotlin
Published: 2020-12-21 12:52:53

You (should) know, Test Driven Development is one of the greatest practice in software development: it gives you confidence in what you implement, it gives you courage to refactor or change behaviors, it forces you to think about what you are about to implement.

For sure not every piece of code deserves to be written test-first, but at least you should start thinking: “Can I write a test before?”.

Passing to an event driven world the process remains the same: 
there’s a [SUT](http://xunitpatterns.com/SUT.html), a trigger action, and some behavior verification:

![](https://cdn-images-1.medium.com/max/7824/1*LbGaYOzMRXDYwOcWErK1eQ.jpeg)

Consider implementing a feature for an hypothetical Continuous Integration application, on tests executed should check the results and notify to the application. This application in based on [Vert.x framework](https://vertx.io/) and is coded in [Kotlin language](https://kotlinlang.org/).

So our first test will be: “*on tests executed, when they passed, should emit tests passed”:*
```kotlin
@ExtendWith(VertxExtension::class)
internal class CheckTestsExecutionTest {

  @Test
  @Timeout(3, timeUnit = SECONDS)
  internal fun `on tests executed, when they passed, should emit tests passed`(vertx: Vertx, context: VertxTestContext) {

    CheckTestsExecution(vertx.eventBus()).start()

    vertx.eventBus().consumer<JsonObject>("TestsPassed") {
      context.completeNow()
    }

    vertx.eventBus()
      .publish("TestsExecuted", JsonObject.mapFrom(
        TestsExecuted(listOf(
          TestResult("simple test", passed = true)
        )))
      )
  }
}
```
There’s a lot of stuff in there. The *VertxExtension *class* *is a junit5 extension provided by the library *vertx-junit5* that gives some facilities to write Vert.x unit tests.

But, the very important thing here is that there’s **no assertion**. The reason is simple: we are testing an **asynchronous behavior**, so we will get no synchronous output from our .

What we need here is to set an **expectation**. To do that we create a consumer on the event we expecting to be launched from our SUT. To make test pass then we call the *completeNow* method on *context* object (is a Vert.x junit5 extension utility to handle asynchronous tests, as well as *@Timeout* annotation).
The test will pass only when *completeNow* method is called before the timeout. Simple.

The last test row emits an event that should trigger our desired behavior.
Obviously, after fixing compilation errors, the test will **fail**. Red bar, cool.

Let’s write **only enough code** to make test pass:
```kotlin
class CheckTestsExecution(private val eventBus: EventBus) {

  fun start() {
    eventBus.consumer<JsonObject>("TestsExecuted") {
      eventBus.publish("TestsPassed", JsonObject())
    }
  } 
}
```

Green bar. Go on.

Now, we need to describe the case where one test fails and so we will expect to receive *TestsFailed* event. We should also expect not to receive *TestsPasses*.
```kotlin
  @Test
  @Timeout(3, timeUnit = SECONDS)
  internal fun `on tests executed, when they failed, should emit tests passed`(vertx: Vertx, context: VertxTestContext) {
    CheckTestsExecution(vertx.eventBus()).start()

    vertx.eventBus().consumer<JsonObject>("TestsPassed") {
      context.failNow(RuntimeException("No TestPassed event should be emitted"))
    }

    vertx.eventBus().consumer<JsonObject>("TestsFailed") {
      context.completeNow()
    }

    val testResults = listOf(
      TestResult("simple test", passed = false)
    )
    vertx.eventBus()
      .publish("TestsExecuted", JsonObject.mapFrom(TestsExecuted(testResults)))
  }
```
Red bar again, so, make it green ASAP!
```kotlin
class CheckTestsExecution(private val eventBus: EventBus) {

  fun start() {
    eventBus.consumer<JsonObject>("TestsExecuted") { message ->
      val testExecuted = message.body().mapTo(TestsExecuted::class.java)
      if (testExecuted.tests.none { !it.passed }) {
        eventBus.publish("TestsPassed", JsonObject())
      } else {
        eventBus.publish("TestsFailed", JsonObject())
      }
    }
  }
  
}
```
Perfect, you just implemented an event driven component with TDD on Vert.x in Kotlin. Refactoring is up to you.

As you see, no great difference from the classic TDD approach: think, write a test, do something and expect behaviors, just pay attention to asynchronous manners and use expectations.