I’m trying to write a REST-API using Ktor and Koin.
I already wrote the authentication part, but when I wonted to test it I couldn’t
get Koin to replace the user repository implementation, my Ktor app is using.
I know Koin has a section on there web side for Koin in tests and I also know that there have been
similar questions on stack overflow (see my research section below), but I just
couldn’t make it work although, I tried several things already. Now I’m confused and don’t know what to do.
Here are some of the materials I visited doing my research:
- Inject a koin database into a ktor testapplication
- Setting up a test with Koin in ktor
- Test Ktor Application with Koin (not embedded)
- https://towardsdev.com/how-to-use-the-koin-framework-in-ktor-applications-c4dc81e7b6bf
- https://insert-koin.io/docs/quickstart/ktor/
- https://youtrack.jetbrains.com/issue/KTOR-5859/How-to-setup-Koin-in-Ktor-using-testApplication
- https://github.com/thomasneuteboom/ktor-koin-example
- https://towardsdev.com/how-to-use-the-koin-framework-in-ktor-applications-c4dc81e7b6bf
- https://ktor.io/docs/server-testing.html
- https://insert-koin.io/docs/reference/koin-test/testing/
Here is also how my last try looks like:
class LoginTest1 : AnnotationSpec(), KoinTest {
val userPaul = User("Paul", "pwd")
val users: MutableSet<User> = HashSet<User>().apply { add(userPaul) }
val userRepo = UserRepositoryImpl(users) // this is just a test in memory implementation using a hash set
// something else I tried, in order to load the alternative koin module
// @BeforeTest
// fun setup(){
// startKoin{ modules(module { single<UserRepository> { userRepo } }) }
// }
// yet another thing I tried
// @JvmField
// @RegisterExtension
// val koinTestExtension = KoinTestExtension.create {
// modules( module { single<UserRepository> { userRepo } } )
// }
@Test
fun `login correctly`() {
testApplication {
application {
koin { // instead of this koin context I also tried to use startKoin {...}
loadKoinModules( // I also tried modules instead of loadKoinModules
module { single<UserRepository> { userRepo } }
)
}
}
/* btw. I can use inject<UserRepository> here inorder to access the alternative implementation declared above,
but it is not injected into my application. */
val client = createClient()
users.forEach { user ->
val response = client.sendLoginRequest(user.name, user.password)
/* This actually works perfectly fine when I just hard code the user into my actual
repository instead of trying to replace the injection */
response.status shouldBe HttpStatusCode.OK
val loginResponse = Json.decodeFromString<LoginResponse>(response.bodyAsText())
with(loginResponse) {
shouldNotBeNull()
token.shouldNotBeEmpty()
}
}
}
}
}
suspend fun HttpClient.sendLoginRequest(username: String, password: String): HttpResponse = post("/api/auth") {
contentType(ContentType.Application.Json)
setBody(LoginRequest(username, password))
}
fun ApplicationTestBuilder.createClient(): HttpClient {
return createClient {
install(ClientContentNegotiation) { json() }
}
}