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 Assert
s 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.
I am Victor Ewert, an Independent Software Developer and owner of Ewert Technologies. In the past I have worked as a Software Tester including working on Software Test automation.
My current technology interests include Java, JavaFX, Kotlin, Swift, Privacy and Security, and Mobile App development.