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()
        // 省略
    }

}

参考