Kotestの条件付きテスト(Conditional evaluation)

テストを無効にする方法はいくつかある。テスト内でハードコードすることも、実行時に条件付きで無効にすることもできる。

Config

Kotestは、テストに設定フラグを指定してテストを無効にできる。この設定フラグには、enabledenabledIfenabledOrReasonIfがある。

enabled flags

config関数のenabledパラメータをfalseに設定すると、テストケースを無効にできる。逆に、trueに設定すると有効になる。

enabledパラメータを設定する例を見てみよう。

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe

class ConfigEnabledTest : StringSpec({

    "常に実行される".config(enabled = true) {
        // このテストは常に実行される。
        1 + 1 shouldBe 2
    }

    "常に実行されない".config(enabled = false) {
        // このテストは実行されない。
        1 + 1 shouldBe 3
    }
})

falseに設定した場合は実行されず、trueに設定した場合は実行される。

同じ仕組みを使用して、特定の条件でのみテストを実行できる。例えば、Apache Commons LangSystemUtils.IS_OS_LINUXを使用して、Linux環境でのみ特定のテストを実行するといった使い方ができる。

"Linuxの場合のみ実行".config(enabled = IS_OS_LINUX) {
    // このテストはLinuxの場合に実行される。
}

"Macの場合のみ実行".config(enabled = IS_OS_MAC) {
    // このテストはMacの場合に実行される。
}

Enabled if

テストが呼び出されるたびに評価される関数を使用したい場合は、enabledIfを使用できる。この関数は(TestCase) -> Boolean形式であり、実行時にテストの有効化または無効化を評価するときにテストへアクセスできる。

例えば、Linux環境ではない場合に、dangerという単語で始まるすべてのテストを無効にしたい場合は、次のようにできる。

import io.kotest.core.spec.style.StringSpec
import io.kotest.core.test.EnabledIf
import org.apache.commons.lang3.SystemUtils.IS_OS_LINUX

class ConfigEnabledIf : StringSpec({

    val disableDangerOnNotLinux: EnabledIf = { !it.name.testName.startsWith("danger") || IS_OS_LINUX }

    "danger will robinson".config(enabledIf = disableDangerOnNotLinux) {
        // test here
    }

    "very safe will".config(enabledIf = disableDangerOnNotLinux) {
        // test here
    }
})

Enabled or Reason If

有効化フラグの3つ目のバリエーションとして、テストが無効化された理由を返せるenabledOrReasonIfがある。このバリエーションは(TestCase) -> Enabled形式であり、ここでEnabledはスキップ理由を含められる型である。この理由文字列はテストレポートに渡される。

例えば、前述のdangerの例は次のように書き換えられる。

import io.kotest.core.spec.style.StringSpec
import io.kotest.core.test.Enabled
import io.kotest.core.test.TestCase
import org.apache.commons.lang3.SystemUtils

class ConfigEnabledOrReasonIfTest : StringSpec({

    val disableDangerOnFridays: (TestCase) -> Enabled = {
        if (it.name.testName.startsWith("danger") || SystemUtils.IS_OS_LINUX)
            Enabled.disabled("It's a linux, and we don't like danger!")
        else
            Enabled.enabled
    }

    "danger Will Robinson".config(enabledOrReasonIf = disableDangerOnFridays) {
        // test here
    }

    "safe Will Robinson".config(enabledOrReasonIf = disableDangerOnFridays) {
        // test here
    }
})

以下の結果がコンソールに表示される。

Process finished with exit code 0

It's a linux, and we don't like danger!

Focus

KotestにおけるFocusは、特定のテストを実行するときに優先順位を与える機能である。Focusを使用すると、特定のテストやテストグループに焦点を当てて実行できる。これにより、開発者は特定の部分に集中し、デバッグや開発をより効率的に進められる。

Focusの設定方法は簡単である。テスト関数やテストグループの前にf:を付けると、そのテストまたはテストグループがFocusされる。つまり、そのテストと、その範囲内に定義されたすべての子テストだけが実行され、残りのテストは実行されない。

例えば、次はFocusを使用したサンプルコードである。

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe

class FocusTest: StringSpec({
    "このテストはFocusされない" {
        // このテストは実行されない。
        1 + 1 shouldBe 2
    }

    // このテストはFocusされて実行される。
    "f:を使用したFocusの例" {
        1 + 1 shouldBe 2
    }

    "このテストもFocusされない" {
        // このテストは実行されない。
        1 + 1 shouldBe 2
    }
})

上のコードでは、"f:を使用したFocusの例"テストだけがFocusされて実行される。残りのテストは実行されない。これにより、開発者は重要な部分だけに集中して、より効率的に開発できる。

また、ネストしたテストは親テストが実行された後にのみ検出されるため、フォーカスモードはネストしたテストには機能しない。

Bang

KotestにおけるBang(!)は、そのテストを無視して実行しないように指定するために使用される。これは、特定のテストを現在の状況で実行すべきでない場合に便利である。特に、開発中のテストを一時的に無効にするときに役立つ。特定のテストを完全に削除せず、あとで再び有効にできる。

Bangはテスト関数やテストグループの前に使用する。

例えば、次はBangを使用したサンプルコードである。

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe

class BangTest : StringSpec({
    "!このテストは実行されない" {
        // このテストは実行されない。
        1 + 1 shouldBe 2
    }

    "このテストは実行される" {
        // このテストは実行される。
        1 + 1 shouldBe 2
    }
})

上のコードでは、接頭辞として!が付いた"!このテストは実行されない"というテストは実行されない。他のテストは実行される。

このようにBangを使用して、特定のテストを一時的に無効にでき、必要になったときに再び有効にできる。

X-Method

KotestにおけるX-Methodは、テストを無効にするために使用されるメタデータである。特定のテスト関数やテストグループの前にxを付けると、そのテストは実行されない。これにより、特定のテストを一時的に無効にし、あとで再び有効にできる。

例えば、次はX-Methodを使用したサンプルコードである。

import io.kotest.core.spec.style.DescribeSpec

class XMethodTest : DescribeSpec({

    xdescribe("このブロックとその子要素は無効化された") {
        it("このテストは実行されない") {
            // disabled test
        }
    }
})

上のコードでは、X-Methodを使用して特定のテストグループとテストを無効にしている。xdescribeは無効化されたグループとテストを表す。残りのテストは実行される。

このようにX-Methodを使用して、特定のテストやテストグループを一時的に無効にでき、あとで必要になったときに再び有効にできる。

@Ignored

Kotestにおける@Ignoredアノテーションは、すべてのテスト関数を無効にするために使用される。このアノテーションを使用すると、そのテスト関数は実行されない。これにより、特定のテストを一時的に無効にし、あとで再び有効にできる。

例えば、次は@Ignoredアノテーションを使用したサンプルコードである。

package com.devkuma.conditional.evaluation

import io.kotest.core.annotation.Ignored
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe

@Ignored
class IgnoredTest : StringSpec({
    "このテストは実行されない" {
        // このテストは実行されない。
        1 + 1 shouldBe 3
    }
}) {
    init {
        error("boom") // Specが作成されないため、このエラーは発生しない。
    }
}

上のコードでは、@Ignoredアノテーションを使用してすべてのテストを無効にしている。

このようにして、特定のテスト関数を一時的に無効にでき、必要になったときに再び有効にできる。

@EnabledIf

Kotestにおける@EnabledIfアノテーションは、特定の条件が真の場合にのみテスト関数を有効にするために使用される。このアノテーションを使用すると、特定の条件を満たす場合にのみテストを実行できる。

@EnabledIfアノテーションを使用するには、EnabledConditionインターフェースを実装するクラスを作成する必要がある。このインターフェースにはisEnabledメソッドが含まれており、このメソッドでは特定の条件を判定して真または偽を返す。

例えば、次は@EnabledIfアノテーションを使用して、特定の条件に応じてテストを有効にするサンプルコードである。

package com.devkuma.conditional.evaluation

import io.kotest.core.annotation.EnabledCondition
import io.kotest.core.annotation.EnabledIf
import io.kotest.core.spec.Spec
import io.kotest.core.spec.style.StringSpec

import io.kotest.matchers.shouldBe
import kotlin.reflect.KClass

@EnabledIf(MyEnabledCondition::class)
class EnabledIfTest2 : StringSpec({
    "このテストは特定の条件が真の場合に実行される" {
        // このテストはMyEnabledConditionで指定した条件が真の場合に実行される。
        1 + 1 shouldBe 2
    }
})

object MyEnabledCondition : EnabledCondition {
    override fun enabled(kclass: KClass<out Spec>): Boolean {
        // ここでは特定の条件を判定し、真または偽を返す。
        return true // 特定の条件が真ならtrueを返してテストを有効にする。
    }
}

上のコードでは、MyEnabledConditionオブジェクトがEnabledConditionインターフェースを実装している。enabledメソッドでは特定の条件を判定し、真または偽を返す。@EnabledIfアノテーションには、この条件を指定するために使用される。したがって、特定の条件が真の場合にのみ、そのテストが実行される。

このようにして、特定の条件に応じてテストを条件付きで有効にでき、テストが実行される条件を柔軟に制御できる。