Kotest Clues

In Kotest, clues let you provide detailed explanations for assertions.

Clues

As a rule of thumb, a failed test should look like a good bug report. It should tell you what went wrong, and ideally why it went wrong.

Sometimes a failed assertion’s error message contains enough information to tell what went wrong.

For example:

username shouldBe "devkuma"

You may get an error like this:

expected:<"devkuma"> but was:<"devkuma@devkuma.com">

If this fails, you may simply get:

<null> should not equal <null>

This is not very helpful. This is where withClue can be used.

The withClue and asClue helpers add extra context to assertions so failures can explain themselves:

For example, you can use withClue with a string message.

withClue("Name should be present") {
    user.name shouldNotBe null
}

This produces an error like:

Name should be present
<null> should not equal <null>

The error message is much better, but still not perfect. For example, if you need to check the database, knowing the user’s ID may help.

You can use asClue to add the user ID to the error message:

withClue({ "Name should be present (user_id=${user.id})" }) {
    user.name shouldNotBe null
}

You can also use the asClue extension function to convert any object into a clue message.

{ "Name should be present (user_id=${user.id})" }.asClue {
    user.name shouldNotBe null
}

This message is generated only when the test fails, so it is safe to use with expensive operations.

You can also use domain objects as clues:

data class HttpResponse(val status: Int, val body: String)

val response = HttpResponse(404, "the content")

response.asClue {
    it.status shouldBe 200
    it.body shouldBe "the content"
}

It prints as follows:

HttpResponse(status=404, body=the content)
Expected :200
Actual   :404

Nested Clues

Clues can be nested, and all of them can be seen in the failed assertion message:

{ "Verifying user_id=${user.name}" }.asClue {
  "email_confirmed should be false since we've just created the user".asClue {
    user.emailConfirmed shouldBe false
  }
  "login".asClue {
    user.login shouldBe "sksamuel"
  }
}

The failure may appear as follows:

Verifying user_id=42
email_confirmed should be false since we've just created the user
<true> should equal <false>

References