KotlinのJUnitTestにおけるアサーションライブラリはKluentがとても良い感じ


JUnitのアサーションを快適に書きたい

KotlinでUnitTestを書いていて、今までさして調べずメジャーだからと言う理由でHamcrestを使ってたんですが、もともとJavaのライブラリなのでKotlinだと微妙に使いにくい部分がありました。

例えば型判定のアサーションを記述したいときは↓のように書きますが

val value = 10
assertThat(value, `is`(instanceOf(Int::class.java)))

Kotlinではisが予約語に指定されているため`で括る必要もあるのと、isinstanceOfがネストしているのも微妙に書きにくい&読みにくく、クラス指定も::class.javaまで記述する必要があって、当たり前ですがKClassで記述できないのでJavaの世界観に引きづられている印象を受けます。

また細かいところだと、isメソッド一つとってもHamcrest内だけでも3つimoport候補が出てきて、どれをimoportするんだっけ?と一瞬手が止まるのも書きにくいなぁと思っていた部分でした(ここはKotlin関係ない部分ですが)

この辺はイマイチだなーと思いつつプロダクションに入らないコードだし動けばまあいいかと思っていたんですが、Kotlinに特化したアサーションライブラリを試しに使ってみたら思いの外快適に記述できて感動したので紹介します。

Kluent


Docs: https://markusamshove.github.io/Kluent/
Github: https://github.com/MarkusAmshove/Kluent

KluentはKotlinに特化したJunitアサーションライブラリです。
Fluent Assertion-Library for Kotlinとあるように流れるように記述できるのが特徴です。

Hamcreststaticメソッドを軸にアサーションを実装しているのに対し、KluentはKotlinの拡張関数infix記法(中置記法)を軸にアサーションが実装されています。

アサーション比較

公式サイトへのリンク先に使い方がわかりやすく乗っているのでぱっと見で理解できるとは思いますが、1例として上記で扱った型判定のアサーションでHamcrestKluentを比較してみます。

Hamcrest
val value = 10
assertThat(value, `is`(instanceOf(Int::class.java)))
Kluent
val value = 10
value shouldBeInstanceOf Int::class

どうでしょうか、カッコもなくなりだいぶ読みやすく書きやすくなったと思います。
Hamcrestはstaticメソッドである関係上Utilクラスのように手前にassertThatメソッドを書かなくてはいけませんが、Kluentはinfix記法のおかげで「valueはint型でなければならない」という英文を違和感なく構築することができています。

もちろんinfix記法でも補完が効きます。とりあえずshould...と打てば必要なものが見つかると思います。

また、infix以外にも拡張関数で定義されたアサーションもあり、nullチェックなどがそれに当たります。

Hamcrest
val value = 10
assertThat(value, `is`(notNullValue()))
Kluent
val value: Int? = 10
value.shouldNotBeNull()

そらで書く場合どちらのほうが記述しやすいかは一目瞭然ですね!
私自身Kluentの基本的な機能しかまだ使っていませんが、サイトのドキュメントを見る限り一般的にアサーションで最低限必要とされるものは揃っていると思います。

モック機能

KluentにはMocking機能があり、サイト内の解説によるとMockito-Kotlinのラッパーとしての役割も担っているそうです。

Kluent
val mock = mock(Database::class)
mock.getPerson(1)

私はMockKに慣れきっていたので使いませんでしたがMockito-Kotlinを使っている方であれば嬉しいポイントかもしれません。

Androidで使う場合

これは正直ちゃんとはわからなかった部分だったのですが、GithubのドキュメントによるとAndroidで使う場合にはartifactが異なり、以下を指定する必要があるそうです。

build.gradle
// for JVM:
testImplementation 'org.amshove.kluent:kluent:{version}'
// for Android:
testImplementation 'org.amshove.kluent:kluent-android:{version}'

この違いについてはドキュメントには記載がなかったのですが、2020/08/08現在のリポジトリ内をざっくり検索したところライブラリの機能や含まれているソースコード自体には違いはなく、JVMによる実行環境とAndroid上の実行環境の差異を吸収するためにartifactが別れているようでした。

これが正しければですが、Androidデバイス上で実行されるInstrumented Testorg.amshove.kluent:kluent-androidを使う必要がありますが、Androidのプロジェクトであってもデバイスを伴わずPC上のJVM環境で実行されるUnitTestRobolectricを使ったテストはorg.amshove.kluent:kluentで十分と思われます。

まとめ

Kluentはいいぞ!