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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?