Should Be
The main matcher or assertion in Kotest is the shouldBe matcher. This matcher is used to assert equality between
an an actual and an expected value. The syntax is in the format actual shouldBe expected For example:
val a = "samuel"
val b = a.take(3)
b shouldBe "sam"
When two values do not compare equal, Kotest will print out a nice error message including an intellij <click to see
difference> between the two values. For example:
Expected :world
Actual :hello
<Click to see difference>
Note, you can check two values are not equal using shouldNotBe.
val a = "samuel"
val b = a.take(3)
b shouldNotBe "bob"
The shouldBe matcher can be combined with power assert for greater effect.
Behind the scenes, Kotest uses the equals method but also adds extra logic to determine equality for some types where
simple object equality isn't quite appropriate. For example, on the JVM, it is well known that Arrays with the same
contents will not be considered equal when using the equals method. Another example is primitives of different types
even with the same value.
This logic is encapsulated in the Eq typeclass which Kotest uses internally. It is also possible to define your own
equality logic for types by implementing Eq and registering it with Kotest.
Custom Eq Instances​
Let's show an example of creating a custom Eq instance for comparing Foo objects. Firstly, the definition of Foo.
data class Foo(val value: String)
Then we implement the Eq typeclass for whatever equality logic we want, returning an EqResult which is either
Success or Failure.
Here are are saying that if one Foo contains the string hello and the other contains the string world then they
are equal. To return a failure message we can use the AssertionErrorBuilder which is a helper to build the
appropriate concrete AssertionError for whichever platform we are running on.
object FooEq : Eq<Foo> {
override fun equals(actual: Foo, expected: Foo, context: EqContext): EqResult {
return if (actual.value == "hello" && expected.value == "world")
EqResult.Success
else EqResult.Failure {
AssertionErrorBuilder.create().withMessage("I don't like foo").build()
}
}
}
If we specify the expected and actual values to the error builder the <click to see difference> link will be
automatically generated too.
Then we register it with Kotest, specifying the type that we want to use it for. Here we are using project config to set it up before any tests are run. We could do this at the spec level too, but bear in mind if you are running tests in parallel then the registration will be non-deterministic.
class ProjectConfig : AbstractProjectConfig() {
override suspend fun beforeProject() {
DefaultEqResolver.register(Foo::class, FooEq)
}
}
Finally, we can use our custom Eq instance in our tests by simply using shouldBe or shouldNotBe as normal.
test("custom eq should be selected if both sides are the same type") {
Foo("hello") shouldBe Foo("world")
}
Custom Eq instances are only selected if both sides of the call are the type specified when registered. Also the type
must be exact, subclasses are not selected automatically and must also be registered