TIL Kotlin data classes make for cleaner aggregate assertions

Oliver Peate posted on March 20th, 2019

Sequential assertions lead to poor failure messages when a test is concerned about a combination of values:


// Expecting:
// <1>
// to be equal to:
// <2>
// but was not within 20 seconds.

The successful count wasn’t 2, but the cause of the failure is unclear. Which bucket did that record end up in?

This failure can be made clearer using aggregate assertions:

assertAll("Import status",
    { assertThat(totalSuccessful).describedAs("success count").isEqualTo(2) },
    { assertThat(totalErrored).describedAs("error count").isEqualTo(0) },
    { assertThat(totalIgnored).describedAs("ignored count").isEqualTo(0) },
    { assertThat(totalInvalid).describedAs("invalid count").isEqualTo(0) }

// org.opentest4j.MultipleFailuresError: Import status (2 failures)
//  [success count] 
// Expecting:
//  <1>
// to be equal to:
//  <2>
// but was not.
//  [invalid count] 
// Expecting:
//  <1>
// to be equal to:
//  <0>
// but was not.

This failure message is clearer. Unfortunately at the moment junit assertAll doesn’t play nicely with awaitility await for making asynchronous assertions.

We can achieve a similar improvement to the failure message using a Kotlin data class instead of assertAll:

data class IngestionResult(
    val successful: Int,
    val errored: Int,
    val ignored: Int,
    val invalid: Int


// Expecting:
//  <IngestionResult(successful=1, errored=0, ignored=0, invalid=1)>
// to be equal to:
//  <IngestionResult(successful=2, errored=0, ignored=0, invalid=0)>
// but was not within 10 seconds.

Kotlin data classes provide a derived equals() and default toString() implementation, the latter giving a reasonable failure message in this case.

What did you learn today?

Write a quick TIL post to reflect on what you've learned.

Write a post