[Kotlin]Ktor 1.6.7 + Koin 3.1.5 + JUnit 5の使用例



# これは何？

Koinを使ったテストでは、公式ドキュメントの通り、JUnit 5では以前のJUnit 4までとは異なる方法で実施する必要があるため、どなたかの参考になればと残しておく。

# 動作環境
+ Kotlin 1.6.10
+ JUnit 5
+ Ktor 1.6.7
+ Koin 3.1.5

# 実装

```build.gradle.kts
dependencies {
    implementation("io.ktor:ktor-server-core:$ktor_version")　 // 1.6.7
    implementation("io.ktor:ktor-server-netty:$ktor_version")
    implementation("io.ktor:ktor-jackson:$ktor_version")
    implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$ktor_version")
    // 省略
    implementation("io.insert-koin:koin-ktor:$koin_version") // 3.1.5
    implementation("io.insert-koin:koin-logger-slf4j:$koin_version")
    testImplementation("org.junit.jupiter:junit-jupiter:5.8.2")
    testImplementation("io.ktor:ktor-server-tests:$ktor_version")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
    testImplementation("io.insert-koin:koin-test:$koin_version")
    testImplementation("io.insert-koin:koin-test-junit5:$koin_version")
}
```

```SamplelistApplication.kt
fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

fun Application.module() {
    configureRouting()
    configureLogging()
    configureSerialization()
    configureStatusPages()
    configureKoin()  //Koinをinstall
}
```

```Koin.kt
import io.ktor.application.*
import org.koin.core.logger.Level
import org.koin.ktor.ext.Koin
import org.koin.logger.slf4jLogger

fun Application.configureKoin() {
    install(Koin) {
        //　KoinのIssue #1188の通り、Ktor 1.6.0以降でKoinにてNoSuchMethodError例外が発生するため、
        //　workaroundとして、ロガーのログレベルにERRORを設定。
        slf4jLogger(level = Level.ERROR)

        modules(sampleRepositoryModule)
    }
}
```

```SampleRepositoryModule.kt
import org.koin.dsl.module

val sampleRepositoryModule = module {
    // SampleRepository     → インターフェース
    // SampleRepositoryImpl → 上記の実装
    factory<SampleRepository> { SampleRepositoryImpl() }
}
```



```SampleUseCase.kt
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject

//　後述のby inject()のためにKoinComponentの継承が必要
class SampleUseCase : KoinComponent {

    //　SampleRepositoryImpがDIされる。
    val sampleRepository by inject<SampleRepository>()

    fun findById(Id: String): SampleUseCaseDto? {
        return sampleRepository.findById(Id)
    }
```

# テストコード

```SampleRepositoryImplTest.kt
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import org.koin.core.component.inject
import org.koin.core.logger.Level
import org.koin.test.KoinTest
import org.koin.test.inject
import org.koin.test.junit5.KoinTestExtension

//　後述のby inject()のためにKoinTestの継承が必要
internal class SampleRepositoryImplTest : KoinTest {

    @JvmField
    @RegisterExtension
    // JUnit 5の場合、JUnit 4までとは異なり、KoinTestRule.createとなっているが
    //　当該バージョンではKoinTestExtension.createに変わっている。
    val koinTestExtension = KoinTestExtension.create {

        //　KoinのIssue #1188の通り、Ktor 1.6.0以降ではKoinにてNoSuchMethodError例外が発生するため、
        //　workaroundとして、ロガーのログレベルにERRORを設定。
        printLogger(level = Level.ERROR)

        modules(sampleRepositoryModule)
    }

    //　SampleRepositoryImplがDIされる。
    val sampleRepository by inject<SampleRepository>()

    @Test
    fun `リポジトリの利用ができる`() {
        sampleRepository.save()
        //　省略
    }

}
```

# 参考

https://insert-koin.io/docs/3.1/reference/koin-test/testing

https://github.com/InsertKoinIO/koin/blob/3.1.5/core/koin-test-junit5/src/test/kotlin/org/koin/test/junit5/DeclareKoinContextFromExtensionTest.kt