Kotest Spring拡張(SpringExtension)

Kotestは、依存性注入のためにSpringフレームワークを使用するコードをテストできるSpring拡張機能を提供する。

Spring Extension

依存関係の追加

この拡張機能を使用するには、テストコンパイルパスにio.kotest.extensions:kotest-extensions-springモジュールを追加する必要がある。最新バージョンはMaven Centralで確認できる。

まずSpringでKotestを使用するには、以下の依存関係を追加する。

testImplementation("io.kotest.extensions:kotest-extensions-spring:1.0.0")

参考: MavenグループIDは、コアテストフレームワーク(io.kotest.extensions)とは異なる。

Configuration

すべてのテストにSpringExtensionを適用するには、グローバル設定で構成できる。

class GlobalProjectConfig : AbstractProjectConfig() {
    override fun extensions() = listOf(SpringExtension)
}

Spring拡張を使用するには、すべてのテストクラスまたはテストクラスごとに有効化する必要がある。

グローバルに有効化するには、プロジェクト構成でSpringExtensionを登録する。

class ProjectConfig : AbstractProjectConfig() {
   override fun extensions() = listOf(SpringExtension)
}

場合によっては、SpringExtensionをグローバルに有効化せずにテストを使用したいこともある。この場合、グローバル設定をせず、SpringExtensionを使用するテストクラスでのみextensionsを設定して使用できる。

テストクラスごとに有効化する方法は次のとおりである。

class SpringTest : FunSpec() {
    override fun extensions() = listOf(SpringExtension)
}

Springで使用する構成クラスを知らせるには、Specクラスに@ContextConfigurationアノテーションを追加する必要がある。このアノテーションは、Springの@Configurationアノテーションが付いたクラスを指す必要がある。または、@ActiveProfilesを使用して特定のアプリケーションコンテキストファイルを指すこともできる。

コンストラクタインジェクション

コンストラクタインジェクションの場合、自動スキャンが有効であると仮定すると、Springモジュールがビルドに追加されたとき、KotestはSpringAutowireConstructorExtensionを自動登録する。自動スキャンが無効の場合は、プロジェクト構成で手動で拡張をロードする必要がある。

この拡張は、Specインスタンスを生成する各呼び出しをインターセプトし、プライマリコンストラクタで宣言されたBeanを自動配線する。

次の例は、プライマリコンストラクタにUserServiceというサービスを必要とするテストクラスである。このサービスクラスは@Componentでアノテーションされた通常のSpring Beanである。

@ContextConfiguration(classes = [(Components::class)])
class SpringAutowiredConstructorTest(service: UserService) : WordSpec() {
  init {
    "SpringExtension" should {
      "have autowired the service" {
        service.repository.findUser().name shouldBe "system_user"
      }
    }
  }
}

@SpringBootTestを使う方法もある。

@SpringBootTest(classes = [FooService::class])
class SpringTest(fooService: FooService) : FunSpec() {
    override fun extensions() = listOf(SpringExtension)

    init {
        test("getFoo test") {
            val actual = fooService.getFoo()
            actual shouldBe Foo("id", "foo")
        }
    }
}

@Service
class FooService {
    fun getFoo(): Foo {
        return Foo("id", "foo")
    }
}

data class Foo(
    val id: String,
    val title: String,
)

テストコンテキスト

Spring拡張は、テストが実行されるコルーチンコンテキストを通じてTestContextManagerを使用できるようにする。このインスタンスへのハンドルはtestContextManager()拡張メソッドで取得できる。

これにより、Springが使用しているテストコンテキストを取得できる。

class MySpec(service: UserService) : WordSpec() {
  init {
    "SpringExtension" should {
      "provide the test context manager" {
         println("The context is " + testContextManager().testContext)
      }
    }
  }
}

テストメソッドコールバック

Springには、テストがメソッドであるという概念に基づいたbeforeTestMethodなど、さまざまなテストコールバックがある。この前提はJUnitのような従来のテストフレームワークには適しているが、テストが関数であるKotestのような現代的なテストフレームワークには適用されない。

したがって、ネストされたSpecスタイルを使用する場合、テストメソッドコールバックが実行されるタイミングをカスタマイズできる。デフォルトではリーフノードである。SpringTestLifecycleMode引数を拡張に渡すことで、ルートノードで実行されるよう設定できる。

class ProjectConfig : AbstractProjectConfig() {
   override fun extensions() = listOf(SpringTestExtension(SpringTestLifecycleMode.Root))
}

ファイナルクラス

ファイナルクラスを使用すると、Kotestから次のような警告を受けることがある。

Using SpringListener on a final class. If any Spring annotation fails to work, try making this class open

必要であれば、システムプロパティkotest.listener.spring.ignore.warningtrueに設定して、この警告を無効化できる。


参照