- Kotlin
Kotestの使い方|スタイル一覧とJUnit・AssertJ対応表
Kotest は、Kotlin 向けのテストフレームワークです。テストを test("名前") { } のような関数で書き、値 shouldBe 期待値 で確かめます。 JVM では JUnit Platform の上で動くので、Gradle の test タスクや IntelliJ IDEA から JUnit と同じように実行できます。
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
class StringTest : FunSpec({
test("length は文字数を返す") {
"sammy".length shouldBe 5
"".length shouldBe 0
}
})
| 項目 | 内容(Kotest 6.2.5 時点) |
|---|---|
| 最新版 | 6.2.5(GitHub のタグ。2026年9月10日) |
| 必要な環境 | JDK 11 以上・Kotlin 2.2 以上(6.0 のリリースノート) |
| 3つの部品 | テストフレームワーク/アサーション(shouldBe など)/プロパティテスト。別々にも使える |
| JUnit との関係 | JVM では JUnit Platform のテストエンジンとして動く |
確認環境:Kotest 6.2.5 のソースコード(GitHub の kotest/kotest、タグ v6.2.5)と、同じリポジトリにあるドキュメント(kotest.io の 6.2 版の元原稿)で、クラス名・関数名・パッケージ・既定値を確かめました。記事のテスト対象のコード(PriceCalculator)は kotlinc 2.4.20(JDK 21)でコンパイルして動かし、テストの期待値(1100・1080・323 と例外メッセージ)になることを確かめました(確認日 2026年10月13日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Kotestの使い方|スタイル一覧とJUnit・AssertJ対応表」
Gradle の設定
ドキュメントの JVM 向けの設定は、JUnit Platform を有効にして、ランナーを足すだけです。アサーションは別のモジュールです。
// build.gradle.kts
dependencies {
testImplementation("io.kotest:kotest-runner-junit5:6.2.5") // テストフレームワーク
testImplementation("io.kotest:kotest-assertions-core:6.2.5") // shouldBe などのマッチャー
testImplementation("io.kotest:kotest-property:6.2.5") // プロパティテスト(使うときだけ)
}
tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
| やりたいこと | 足すもの |
|---|---|
| Kotest でテストを書いて動かす(JVM) | kotest-runner-junit5 と useJUnitPlatform() |
| JUnit のまま、マッチャーだけ使う | kotest-assertions-core だけ |
| IntelliJ からテスト単位で実行・ジャンプする機能を強める | Gradle プラグイン io.kotest(JVM だけなら必須ではない) |
| Kotlin/JS・Native・Wasm | io.kotest プラグイン + KSP プラグイン + kotest-framework-engine |
Kotest 6.0 から、データ駆動テスト(後述の withTests)は本体に入りました。 5.x で使っていた kotest-framework-datatest は外すよう、ドキュメントに書かれています。
Maven では、ドキュメントのアサーションの例のように、成果物名の末尾に -jvm を付けます(kotest-assertions-core-jvm など)。
完成コード:テスト対象とテスト
テスト対象(src/main/kotlin/PriceCalculator.kt)です。
class PriceCalculator {
fun total(prices: List<Int>, taxRate: Double): Int {
require(prices.isNotEmpty()) { "商品がありません" }
return Math.round(prices.sum() * (1 + taxRate)).toInt()
}
}
テスト(src/test/kotlin/PriceCalculatorTest.kt)です。普通のテスト・例外・データ駆動・前処理を1つにまとめています。
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.datatest.withTests
import io.kotest.matchers.shouldBe
class PriceCalculatorTest : FunSpec({
lateinit var calc: PriceCalculator
beforeEach { // JUnit の @BeforeEach
calc = PriceCalculator()
}
test("合計に税を足す") {
calc.total(listOf(500, 500), 0.1) shouldBe 1100
}
test("商品が空なら IllegalArgumentException") {
val e = shouldThrow<IllegalArgumentException> {
calc.total(emptyList(), 0.1)
}
e.message shouldBe "商品がありません"
}
context("税率ごとの合計") { // JUnit の @Nested
withTests(
Triple(listOf(1000), 0.08, 1080),
Triple(listOf(1000), 0.10, 1100),
Triple(listOf(98, 98, 98), 0.10, 323),
) { (prices, rate, expected) ->
calc.total(prices, rate) shouldBe expected
}
}
})
withTests は、渡した行の数だけテストを作ります。 ドキュメントでは、失敗した行は Test failed for ... expected:<9> but was:<41> のように、入力の値と一緒に表示されると説明されています。
スペックスタイル一覧(10種類)
Kotest は、テストの書き方(スタイル)を選べます。 ドキュメントには「スタイルの間に機能の違いは無い。好みの問題」とあり、迷ったら FunSpec が勧められています。
| スタイル | 書き方(キーワード) | 入れ子 | 近い書き方 |
|---|---|---|---|
| FunSpec | context("…") { test("…") { } } |
できる | ScalaTest。迷ったらこれ |
| StringSpec | "名前" { } |
できない(一番上だけ) | 最も短い |
| ShouldSpec | context("…") { should("…") { } } |
できる | FunSpec の test が should |
| DescribeSpec | describe("…") { it("…") { } } |
できる(describe は必須) |
Jest・RSpec |
| BehaviorSpec | given / When / then の3段 |
できる | BDD・Gherkin |
| WordSpec | "…" should { "…" { } } |
できる(When で3段) |
ScalaTest |
| FreeSpec | "…" - { "…" { } } |
何段でも | ScalaTest |
| FeatureSpec | feature("…") { scenario("…") { } } |
できる | Cucumber |
| ExpectSpec | context("…") { expect("…") { } } |
できる | Kotest 独自 |
| AnnotationSpec | @Test fun 名前() { } |
@Nested |
JUnit。JVM だけ |
ドキュメントの表は AnnotationSpec を除く9種類です。AnnotationSpec は JVM 用のソース(jvmCommonMain)にあり、@Test・@BeforeEach・@AfterEach・@BeforeAll・@AfterAll・@Ignore・@Nested などの注釈を持っています。JUnit から少しずつ移すときの足場に使えます。
主なスタイルの例
// StringSpec:一番上にテストを並べるだけ
class StringSpecExample : StringSpec({
"length は文字数を返す" {
"sammy".length shouldBe 5
}
})
// DescribeSpec:describe / it
class DescribeSpecExample : DescribeSpec({
describe("String.length") {
it("文字数を返す") { "sammy".length shouldBe 5 }
it("空文字は 0") { "".length shouldBe 0 }
}
})
// BehaviorSpec:when は Kotlin の予約語なので `when` か When と書く
class BehaviorSpecExample : BehaviorSpec({
given("カートに 500 円の商品が2つ") {
When("税率 10% で合計する") {
then("1100 円になる") {
PriceCalculator().total(listOf(500, 500), 0.1) shouldBe 1100
}
}
}
})
// FreeSpec:- で何段でも入れ子にできる。一番内側には - を付けない
class FreeSpecExample : FreeSpec({
"PriceCalculator" - {
"total" - {
"税を足す" { PriceCalculator().total(listOf(500, 500), 0.1) shouldBe 1100 }
}
}
})
StringSpec のソース(StringSpecRootScope)は一番上のテストだけを定義しています。ドキュメントにも「入れ子にしたいなら、同じ書き方で入れ子ができる FreeSpec を勧める」とあります。
マッチャー早見表
kotest-assertions-core のマッチャーです。関数ごとにパッケージが分かれているので、import をまとめて載せます。 IDE の補完でも入ります。
基本・null・型・例外
| Kotest | 意味 | import |
|---|---|---|
a shouldBe b / a shouldNotBe b |
等しい/等しくない | io.kotest.matchers.shouldBe |
x.shouldBeNull() / x.shouldNotBeNull() |
null である/ない | io.kotest.matchers.nulls.* |
flag.shouldBeTrue() |
true である | io.kotest.matchers.booleans.shouldBeTrue |
x.shouldBeInstanceOf<T>() |
T かその子クラス | io.kotest.matchers.types.shouldBeInstanceOf |
x.shouldBeTypeOf<T>() |
ちょうど T(子クラスは不可) | io.kotest.matchers.types.shouldBeTypeOf |
a shouldBeSameInstanceAs b |
同じインスタンス | io.kotest.matchers.types.shouldBeSameInstanceAs |
shouldThrow<E> { } |
E かその子クラスが投げられる。例外を返す | io.kotest.assertions.throwables.shouldThrow |
shouldThrowExactly<E> { } |
ちょうど E が投げられる | io.kotest.assertions.throwables.shouldThrowExactly |
shouldThrowWithMessage<E>("…") { } |
型とメッセージが一致 | io.kotest.assertions.throwables.shouldThrowWithMessage |
shouldNotThrowAny { } |
何も投げない | io.kotest.assertions.throwables.shouldNotThrowAny |
e shouldHaveMessage "…" |
例外のメッセージ | io.kotest.matchers.throwable.shouldHaveMessage |
文字列(io.kotest.matchers.string.*)
| Kotest | 意味 |
|---|---|
s shouldStartWith "ab" / s shouldEndWith "yz" |
前方一致/後方一致 |
s shouldContain "abc" |
含む(大文字・小文字を区別) |
s shouldContainIgnoringCase "abc" |
大文字・小文字を区別せずに含む |
s shouldMatch "[0-9]{3}-[0-9]{4}" |
正規表現に全体が一致 |
s shouldHaveLength 5 |
長さ |
s.shouldBeEmpty() / s.shouldBeBlank() |
空文字/空白だけ |
s shouldBeEqualIgnoringCase "ABC" |
大文字・小文字を無視して等しい |
コレクション(io.kotest.matchers.collections.*)・Map(io.kotest.matchers.maps.*)
| Kotest | 意味 |
|---|---|
list shouldHaveSize 3 |
要素数 |
list.shouldBeEmpty() |
空 |
list shouldContain "a" |
含む |
list.shouldContainAll("a", "b") |
すべて含む(順不同・ほかがあってもよい) |
list.shouldContainExactly("a", "b") |
これだけ・この順 |
list.shouldContainExactlyInAnyOrder("b", "a") |
これだけ・順不同 |
list shouldContainInOrder listOf("a", "c") |
この順で含む(間にほかがあってもよい) |
list.shouldBeSorted() / list.shouldBeUnique() |
並んでいる/重複なし |
list shouldHaveSingleElement "a" |
1つだけで、それが "a" |
map.shouldContain("k", "v") / map shouldContainKey "k" |
キーと値/キー |
数値・日付
| Kotest | 意味 | import |
|---|---|---|
n shouldBeGreaterThan 0 / n shouldBeLessThan 10 |
より大きい/小さい | io.kotest.matchers.ints.* |
n.shouldBeBetween(1, 10) |
1 以上 10 以下 | io.kotest.matchers.ints.shouldBeBetween |
d shouldBe (0.3 plusOrMinus 0.0001) |
小数を誤差つきで比べる | io.kotest.matchers.doubles.plusOrMinus |
date shouldBeBefore other / shouldBeAfter |
日付の前後(LocalDate など) |
io.kotest.matchers.date.* |
Long は io.kotest.matchers.longs、Double は io.kotest.matchers.doubles のように、型ごとにパッケージが分かれています。ドキュメントでは、Double の比較には plusOrMinus を使う方法が勧められています。
複数をまとめて確かめる・失敗時の説明を足す
import io.kotest.assertions.assertSoftly
import io.kotest.assertions.withClue
assertSoftly { // 途中で失敗しても最後まで確かめ、まとめて報告
user.name shouldBe "佐藤"
user.age shouldBe 30
}
withClue("ID=${user.id} の名前") { // 失敗時のメッセージに説明を足す
user.name shouldBe "佐藤"
}
JUnit 5 からの書き換え対応表
| JUnit 5 | Kotest(FunSpec) |
|---|---|
@Test fun 名前() |
test("名前") { } |
@DisplayName("…") |
テスト名の文字列そのもの |
@Nested inner class |
context("…") { } |
@BeforeEach / @AfterEach |
beforeEach { } / afterEach { } |
@BeforeAll / @AfterAll |
beforeSpec { } / afterSpec { } |
@Disabled |
xtest("…")、.config(enabled = false)、名前の先頭に ! |
@ParameterizedTest + @CsvSource など |
withTests(…) / withContexts(…) |
@RepeatedTest(10) |
test("…").config(invocations = 10) { } |
@Timeout(2) |
test("…").config(timeout = 2.seconds) { } |
@Tag("slow") |
.config(tags = setOf(Slow))。実行時に kotest.tags か KOTEST_TAGS で選ぶ |
assumeTrue(…) |
.config(enabledIf = { … }) |
@ExtendWith(SpringExtension::class) |
@ApplyExtension(SpringExtension::class)(kotest-extensions-spring) |
| 1つのテストだけ動かす | 名前の先頭に f:(一番上のテストだけ) |
beforeTest は、ドキュメントでは beforeAny の別名で、context などの入れ物(コンテナー)の前でも呼ばれます。テスト1件ごとの前処理には beforeEach が JUnit の @BeforeEach に近い動きです。
import io.kotest.core.Tag
import kotlin.time.Duration.Companion.seconds
object Slow : Tag()
class ConfigExample : FunSpec({
test("重い処理は 2 秒以内").config(timeout = 2.seconds, tags = setOf(Slow)) {
// …
}
xtest("いったん止めるテスト") {
// …
}
})
いちばん違う点:テストクラスのインスタンスは1つ
JUnit 5 は、既定でテストメソッドごとにテストクラスを作り直します。Kotest の既定(SingleInstance)は、1つのスペックで1つのインスタンスを使い回します。 ドキュメントにも「既定は SingleInstance で、JUnit とは違う」とあります。
class CartTest : FunSpec({
val cart = mutableListOf<String>() // 全テストで同じリスト
test("1つ入れる") {
cart += "りんご"
cart shouldHaveSize 1
}
test("空から始まる?") {
cart.shouldBeEmpty() // ← 前のテストの「りんご」が残る
}
})
JUnit のつもりで書くと、このように前のテストの状態が残ります。直し方は2つです。
| 直し方 | 書き方 |
|---|---|
| テストごとに作り直す | lateinit var + beforeEach { cart = mutableListOf() }(上の完成コードの形) |
| 一番上のテストごとにインスタンスを作る | isolationMode = IsolationMode.InstancePerRoot(スペック内、または AbstractProjectConfig) |
Kotest 6.0 のリリースノートでは、InstancePerRoot が新しく加わり、InstancePerTest と InstancePerLeaf は「端の場面で動きが定まらない」ため非推奨になりました。
AssertJ からの書き換え対応表
| AssertJ | Kotest |
|---|---|
assertThat(a).isEqualTo(b) |
a shouldBe b |
assertThat(a).isNotEqualTo(b) |
a shouldNotBe b |
assertThat(x).isNull() |
x.shouldBeNull() |
assertThat(flag).isTrue() |
flag.shouldBeTrue() |
assertThat(x).isInstanceOf(Foo::class.java) |
x.shouldBeInstanceOf<Foo>() |
assertThat(s).startsWith("ab") |
s shouldStartWith "ab" |
assertThat(s).contains("ab") |
s shouldContain "ab" |
assertThat(s).matches("…") |
s shouldMatch "…" |
assertThat(list).hasSize(3) |
list shouldHaveSize 3 |
assertThat(list).containsExactly("a", "b") |
list.shouldContainExactly("a", "b") |
assertThat(list).containsExactlyInAnyOrder(…) |
list.shouldContainExactlyInAnyOrder(…) |
assertThat(list).contains("a", "b") |
list.shouldContainAll("a", "b") |
assertThat(map).containsEntry("k", "v") |
map.shouldContain("k", "v") |
assertThat(n).isBetween(1, 10) |
n.shouldBeBetween(1, 10) |
assertThat(d).isCloseTo(0.3, within(0.0001)) |
d shouldBe (0.3 plusOrMinus 0.0001) |
assertThatThrownBy { … }.isInstanceOf(E::class.java) |
shouldThrow<E> { … } |
….hasMessage("m") |
shouldThrowWithMessage<E>("m") { … } |
assertThatCode { … }.doesNotThrowAnyException() |
shouldNotThrowAny { … } |
SoftAssertions.assertSoftly { … } |
assertSoftly { … } |
assertThat(a).as("説明").isEqualTo(b) |
withClue("説明") { a shouldBe b } |
AssertJ 側の書き方は、関連記事「AssertJの使い方|assertThatの早見表(文字列・リスト・例外・日付)」で扱っています。
よくある質問
Q. JUnit と Kotest を同じプロジェクトで混ぜられますか。
A. どちらも JUnit Platform のテストエンジンなので、useJUnitPlatform() の設定で両方のテストが動く構成にできます。少しずつ移すなら、JUnit の書き方に近い AnnotationSpec から始める方法もあります。JUnit 側の書き方は、関連記事「JUnitの使い方|単体テストの書き方と観点【JUnit 5・6】」で扱っています。
Q. JUnit 6 で動かせますか。
A. Kotest 6.2.5 のソースには kotest-runner-junit5 のほかに kotest-runner-junit6 というモジュールがあり、JUnit Platform 6.0.3 を使う設定でした。ビルド設定のコメントには「JUnit 6 は JDK 17 以上」とあります。ドキュメントの設定手順に載っているのは kotest-runner-junit5 です。
Q. モックはどうしますか。 A. Kotest にはモックの機能は無く、Kotlin では MockK と組み合わせる例が多くあります。関連記事「MockK 使い方|every・verify・coEveryとMockito対応表」で扱っています。
Q. Gradle で何度実行しても「テストが動かない」ように見えます。
A. ドキュメントでは、ソースが変わっていないと Gradle が結果をキャッシュし、テストを実行しなかったと表示する、と注意されています。ドキュメントには、ビルドファイルを変えずに1回だけ実行し直す方法として ./gradlew test --rerun が載っています。
Q. コルーチンの suspend 関数をテストできますか。
A. テストの本体(test("…") { } のブロック)は suspend のラムダなので、中で suspend 関数をそのまま呼べます(FunSpecRootScope のソースで、引数が suspend TestScope.() -> Unit)。
まとめ
- Kotest 6.2.5 は JDK 11 以上・Kotlin 2.2 以上。JVM では
kotest-runner-junit5+useJUnitPlatform() - スタイルは 10種類で機能は同じ。迷ったら FunSpec、短く書くなら StringSpec
shouldBe・shouldThrow・shouldContainExactlyなどはパッケージが分かれているので import に注意- 既定は1つのインスタンスを使い回す(SingleInstance)。JUnit と違い、状態が次のテストに残る
- パラメーター化は
withTests(6.0 から本体に同梱)、無効化はxtest、1つだけ実行はf:
参考・出典
確認日はいずれも 2026年10月13日です。
- Kotest ドキュメント「Testing Styles」:https://kotest.io/docs/framework/testing-styles.html
- Kotest ドキュメント「Setup」:https://kotest.io/docs/framework/project-setup.html
- Kotest ドキュメント「Core Matchers」:https://kotest.io/docs/assertions/core-matchers.html
- Kotest ドキュメント「Isolation Modes」:https://kotest.io/docs/framework/isolation-mode.html
- Kotest ドキュメント「Data Driven Testing」:https://kotest.io/docs/framework/datatesting/data-driven-testing.html
- Kotest ドキュメント「Lifecycle Hooks」:https://kotest.io/docs/framework/lifecycle-hooks.html
- Kotest(GitHub・タグ v6.2.5 のソースと documentation):https://github.com/kotest/kotest
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Kotestの使い方|スタイル一覧とJUnit・AssertJ対応表」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。