Kotestタイムアウト(timeouts)

テストに呼び出し時間制限を設定してテストする方法について説明する。

テストタイムアウト

Kotestは2種類のテスト時間制限をサポートする。1つ目は、テストのすべての呼び出しに対する全体時間である。これをタイムアウトと呼ぶ。2つ目は、テストの個別実行ごとの時間であり、これを呼び出しタイムアウトと呼ぶ。

テストタイムアウト

テストタイムアウトを設定するには、テスト構成を使用する。

class TimeoutTest : FunSpec({
   test("this test will timeout quickly!").config(timeout = 100.milliseconds) {
      // test here
   }
})

また、Specファイル内のすべてのテストにテストタイムアウトを適用することもできる。

class TimeoutTest : FunSpec({

   timeout = 100.milliseconds

   test("this test will timeout quickly!") {
      // test here
   }

   test("so will this one!") {
      // test here
   }
})

呼び出しタイムアウト

Kotestはテストを複数回呼び出すよう構成できる。例:

class TimeoutTest : DescribeSpec({

   describe("my test context") {
        it("run me three times").config(invocations = 3) {
            // this test will be invoked three times
        }
   }

})

その後、invocationTimeoutプロパティを使って呼び出しごとのタイムアウトを適用できる。

class TimeoutTest : DescribeSpec({

   describe("my test context") {
        it("run me three times").config(invocations = 3, invocationTimeout = 60.milliseconds) {
            // this test will be invoked three times and each has a timeout of 60 milliseconds
        }
   }

})

前の例では、各呼び出しは60ミリ秒以内に完了しなければならない。これを全体のテストタイムアウトと組み合わせることができる。

class TimeoutTest : DescribeSpec({

   describe("my test context") {
        it("run me three times").config(timeout = 100.milliseconds, invocations = 3, invocationTimeout = 60.milliseconds) {
            // this test will be invoked three times
        }
   }

})

ここでは、3つのテストすべてが100ミリ秒以内に完了するようにしつつ、特定の呼び出しは最大60ミリ秒まで延長できるようにしている。

テストタイムアウトと同様、Specレベルで呼び出しタイムアウトを適用できる。

class TimeoutTest : FunSpec({

   invocationTimeout = 25.milliseconds

   test("foo") {
      // test here
   }

   test("bar") {
      // test here
   }
})

プロジェクト全体設定

プロジェクト構成を使用して、モジュール内のすべてのテストにテストタイムアウトや呼び出しタイムアウトを適用できる。

object ProjectConfig : AbstractProjectConfig {
    override val timeout = 100.milliseconds
    override val invocationTimeout = 33.milliseconds
}

これらの値は、Specまたはテストレベルで再定義しない限り適用される。

システムプロパティ

テストタイムアウトと呼び出しタイムアウトはどちらもシステムプロパティを使って設定でき、値はミリ秒単位で指定できる。

  • kotest.framework.timeoutは結合されたテストタイムアウトを設定する。
  • kotest.framework.invocation.timeoutは呼び出しテストタイムアウトを設定する。

プロジェクトタイムアウト

Kotestはプロジェクトレベルのタイムアウトをサポートする。このタイムアウトはモジュール内のすべてのテストに適用され、モジュール内のすべてのSpecやテストのセットアップ/後処理時間を含む。

有効にするには、ProjectConfigを使用すればよい。

class ProjectConfig : AbstractProjectConfig() {
  override val projectTimeout: Duration = 10.minutes
}

上の例では、プロジェクトタイムアウトを10分に指定している。すべてのSpecとテストは10分以内に完了しなければならず、そうでなければビルドは失敗する。

ブロッキングテスト

テストでタイムアウトを指定するとき、KotestはKotlinコルーチンライブラリが提供するwithTimeoutコルーチン関数を使用する。これらのタイムアウトは本質的に協調的であり、コルーチンが一時停止、再開、またはyieldを呼び出すときにタイムアウトが検出される。

しかし、ブロッキングコードを実行するときはスレッドがブロックされるため、協調的なアプローチは機能しない。このシナリオでは、Thread.interruptまたはそれに類似するものを使ってスレッドを中断する方法に戻る必要がある。この中断が安全に動作するには、テストを専用スレッドで実行する必要がある。

したがって、特定のテストを中断に安全に使える専用スレッドで実行したいことをKotestに知らせるのはユーザーの責任である。テスト構成でブロッキングテストフラグを有効にすればよい。

例:

class MyBlockingTest : FunSpec() {
  init {

    test("interrupt me!").config(blockingTest = true, timeout = 10.seconds) {
       Thread.sleep(100000000)
    }

    test("uses suspension").config(timeout = 10.seconds) {
      delay(100000000)
    }
  }
}

上の例では、最初のテストはスレッドブロッキング操作を使用するため、ブロッキングテストフラグが必要である。2番目のテストは一時停止可能な操作を使用するため必要ない。


参照