Skip to content

Assertions

Kotest is split into several subprojects which can be used independently. One of these subprojects is the comprehensive assertion / matchers support. These can be used with the Kotest test framework, or with another test framework like JUnit or Spock.

Matchers

The core functionality of the assertion modules is without doubt the statements that confirm that your test is in the state you expect. For example, to assert that a variable has an expected value:

name shouldBe "sam"

Kotest calls these functions matchers.

There are general purpose matchers, such as shouldBe as well as matchers for many other specific scenarios, such as str.shouldHaveLength(10) and file.shouldBeDirectory(). They come in both infix and regular variants.

There are over 325 matchers spread across multiple modules. Read about all the matchers here.

Custom Matchers

It is easy to add your own matchers. Simply extend the Matcher<T> interface, where T is the type you wish to match against. The Matcher interface specifies one method, test, which you must implement returning an instance of Result. The Result contains a boolean to indicate if the test passed or failed, and two messages.

interface Matcher<in T> {
   fun test(value: T): MatcherResult
}

Matcher is contravariant so a matcher for Number can be used to test an Int, for example.

The first message should always be in the positive, ie, indicate what "should" happen, and the second message is used when the matcher is used with not.

For example to create a matcher that checks that a string contains the substring "foo", we can do the following:

fun containFoo() = object : Matcher<String> {
  override fun test(value: String) = MatcherResult(value.contains("foo"), "String $value should include foo", "String $value should not include foo")
}

This matcher could then be used as follows:

"hello foo" should containFoo()
"hello bar" shouldNot containFoo()

And we should then create an extension function version, like this:

fun String.shouldContainFoo() = this should containFoo()
fun String.shouldNotContainFoo() = this shouldNot containFoo()

Exceptions

To assert that a given block of code throws an exception, one can use the shouldThrow function. Eg,

shouldThrow<IllegalAccessException> {
  // code in here that you expect to throw an IllegalAccessException
}

You can also check the caught exception:

val exception = shouldThrow<IllegalAccessException> {
  // code in here that you expect to throw an IllegalAccessException
}
exception.message should startWith("Something went wrong")

If you want to test that exactly a type of exception is thrown, then use shouldThrowExactly<E>. If you want to test that any exception is thrown, then use shouldThrowAny.

Soft Assertions

Normally, assertions like shouldBe throw an exception when they fail. But sometimes you want to perform multiple assertions in a test, and would like to see all of the assertions that failed. Kotest provides the assertSoftly function for this purpose.

assertSoftly {
  foo shouldBe bar
  foo should contain(baz)
}

If any assertions inside the block failed, the test will continue to run. All failures will be reported in a single exception at the end of the block.

Another version of assertSoftly takes a test target and lambda with test target as its receiver.

assertSoftly(foo) {
    shouldNotEndWith("b")
    length shouldBe 3
}

We can configure assert softly to be implicitly added to every test via project config.

Inspectors

Inspectors allow us to test elements in a collection, and assert the quantity of elements that should be expected to pass (all, none, exactly k and so on). For example

mylist.forExactly(3) {
    it.city shouldBe "Chicago"
}

Read about inspectors here

Clues

Sometimes a failed assertion contains enough information in the error message to know what went wrong, and other times the failure just shows that two values didn't match up.

For example,

user.name shouldNotBe null

If this failed, you would simply get:

<null> should not equal <null>

Which isn't particularly helpful. We can add extra context to failure messages through the use of clues.

Assertion Mode

If you are using Kotest framework alongside Kotest assertions, you can ask Kotest to fail the build, or output a warning to stderr, if a test is executed that does not execute an assertion.

To do this, set assertionMode to AssertionMode.Error or AssertionMode.Warn inside a spec. For example.

class MySpec : FunSpec() {
   init {
      assertions = AssertionMode.Error
      test("this test has no assertions") {
         val name = "sam"
         name.length == 3 // this isn't actually testing anything
      }
   }
}

Running this test will output something like:

Test 'this test has no assertions' did not invoke any assertions

If we want to set this globally, we can do so in project config or via the system property kotest.framework.assertion.mode.

Warning

Assertion mode only works for Kotest assertions and not other assertion libraries.