Kotlin Logo

Custom Asserts for use with Result type in Kotlin

I have recently been working on a project written in Kotlin, and for unit testing, I decided to go with assertk as my assertion library. I have used AssertJ in the past and like the fluent syntax provided by both. With assertk, you can write assertions like:

assertThat(person.name).isEqualTo("Alice")
assertThat(person).prop(Person::age).isEqualTo(20)
assertThat(person.isActive).isTrue()
assertThat(employeeList).hasSize(5)

In this project, I am also using Typed Errors (instead of Exceptions) for error handling and decided to use the kotlin-result library for this. This library provides a Result<V, E> type, so when a function can produce an error it returns a Result, that holds either the value or the error. Below is a simple example:

fun getUser(credential: Credential, id: Int): Result<User, Error> {
  val success = checkCredential(credential)
  return if (success) {
    Ok(User(id))
  } else {
    Err(Error(msg = "Could not create a user!")
  }
}

See the kotlin-result documentation for more examples.

While writing unit tests for functions like this I realized it would be helpful if there were asserts that I could use to check whether the Result was Ok or Err. Luckily, assertk provides a mechanism to create custom asserts and so I created the following Asserts to work with kotlin-result Result<V, E> types.

This first Assert checks that a Result is the Ok (i.e. success) value.

/**
 * Asserts that a [Result] is Ok
 */
fun <V, E> Assert<Result<V, E>>.isOk() = given { actual ->
  if (actual !is Ok) {
    expected("$actual to be Result.Ok}")
  }
}

// Example
val userResult: Result<User, Error> = getUser()
assertThat(userResult).isOk

The second Assert asserts that the Result is the Err value (i.e. failure case)

/**
 * Asserts that a [Result] is an Err
 */
fun <V, E> Assert<Result<V, E>>.isErr() = given { actual ->
  if (actual !is Err) {
    expected("${show(actual)} to be Result.Err")
  }
}

// Example that is expected to give an error:
val userResult: Result<User, Error> = getUser()
assertThat(userResult).isErr

Sometimes it is useful to assert something on the value after checking the Result, so I also created the following, that allows you to chain additional asserts after making the initial assert:

/**
 * Asserts that a [Result] is Ok, and then chains the Ok result
 * so further asserts can be done on the Ok Value
 */
fun <V, E> Assert<Result<V, E>>.isOkAnd(): Assert<V> = transform(appendName("Result.Ok value", ".")) { actual ->
  if (actual is Ok) {
    actual.value
  } else {
    expected("${show(actual)} to be Result.Ok}")
  }
}

// Example
val userResult<User, Errror> = getUser()
assertThat(userResult).isOkAnd().prop(User::name).isEqualTo("Alice")
/**
 * Asserts that a [Result] is an Err and then chains the Err result
 * so further asserts can be done on the Err Value
 */
fun <V, E> Assert<Result<V, E>>.isErrAnd() = transform { actual ->
  if (actual is Err) {
    actual.error
  } else {
    expected("${show(actual)} to be Result.Err")
  }
}

// Example
val userResult<User, Error> = getUser()
assertThat(userResult).isErrAnd().prop(Error::msg).isEqualTo("Could not find the user!")

If you are using assertk with kotlin-result, hopefully, you will find these custom assertions helpful. Please feel free to use them in your own code and/or adapt them as needed. They could also be adapted for other Typed Error mechanisms including the Arrow-Kt Either type.

Leave a Comment

Scroll to Top