Levi
12/18/2023, 8:27 PMobject
singleton, but I'd like to keep things more testable by providing concrete implementations of the environment/dependencies and pass to my route functions all the way from the Application.module()
level. Hopefully that questions make sense. tl;dr looking for whether there's some ktor/kotllin-idiomatic way to enable routes throughout my application to access a particular instance of some dependency.Shawn
12/18/2023, 8:32 PMLevi
12/18/2023, 8:39 PMShawn
12/18/2023, 8:44 PMApplication.module()
function. In your test code, you pass in mocks/stubs where appropriate.Levi
12/18/2023, 8:50 PMApplication.module()
I wire up the dependencies and pass them through, i.e.:
fun Application.module() {
val auth0Client = Auth0Client(environment.config)
val userRepository = UserRepository() // args can be provided here to control the concrete behavior
userRoutes(auth0Client, userRepository)
}
I think what I'm missing is how to swap out those dependencies with the ones I'd like to test with. For example, if I have a test:
@Test
fun testRoot() = testApplication {...}
how do I set up the test to use an Auth0Client
, UserRepository
, etc. that I'd like to use for testing?Shawn
12/18/2023, 8:59 PMLevi
12/18/2023, 9:03 PMLevi
12/18/2023, 9:03 PMLevi
12/18/2023, 9:04 PMShawn
12/18/2023, 9:05 PMShawn
12/18/2023, 9:05 PMLevi
12/18/2023, 9:05 PMLevi
12/18/2023, 9:05 PMShawn
12/18/2023, 9:11 PMShawn
12/18/2023, 9:11 PMShawn
12/18/2023, 9:14 PMShawn
12/18/2023, 9:15 PMLevi
12/18/2023, 11:43 PMLevi
12/19/2023, 2:33 PMtestApplication
since I'd just be testing small public functions and such, right?Shawn
12/19/2023, 7:53 PMLevi
12/19/2023, 7:58 PMLevi
12/19/2023, 7:58 PMShawn
12/19/2023, 8:00 PMShawn
12/19/2023, 8:00 PMLevi
12/19/2023, 8:05 PMShawn
12/19/2023, 8:46 PMtestApplication
for a unit testβall you need is the class you're testing and the mocks it needs for instantiationLevi
12/20/2023, 6:06 PMLevi
12/20/2023, 6:23 PMsrc/test
to match src/main
?Shawn
12/20/2023, 6:25 PMLevi
12/20/2023, 6:26 PMLevi
12/20/2023, 6:26 PMShawn
12/20/2023, 6:27 PMLevi
12/20/2023, 6:27 PMShawn
12/20/2023, 6:28 PMLevi
12/20/2023, 6:28 PM.unit
or .integration
to split test types. Thoughts on that? Bad idea?Shawn
12/20/2023, 6:30 PMFoo
, my unit test is usually called FooTest
and the integration test FooIntegrationTest
Levi
12/20/2023, 6:41 PMsrc/main
dir structure looks like this right now:
app/src/main/kotlin
βββ Application.kt
βββ auth0
β βββ Auth0Client.kt
β βββ Auth0UserType.kt
β βββ Token.kt
βββ cache
β βββ ResidentCache.kt
βββ kafka
β βββ Consumer.kt
β βββ Producer.kt
β βββ Props.kt
β βββ cache
β β βββ KafkaResidentCache.kt
β βββ model
β β βββ Resident.kt
β β βββ ResidentRegisteredEvent.kt
β β βββ SignupUserData.kt
β βββ user
βββ routing
β βββ health
β β βββ check
β β βββ CheckHealthRoute.kt
β βββ user
β βββ UserRoutes.kt
β βββ authenticate
β β βββ A.kt
β β βββ B.kt
β βββ completeRegistration
β β βββ C.kt
β β βββ D.kt
β βββ sendOtpEmail
β β βββ Foo.kt
β β βββ Bar.kt
β βββ sendSmsOtp
β β βββ Foo.kt
β β βββ Bar.kt
β βββ validateEmailOtp
β βββ Foo.kt
β βββ Bar.kt
βββ validation
βββ Extensions.kt
βββ RequestValidations.kt
I have followed the convention here https://kotlinlang.org/docs/coding-conventions.html#directory-structure, i.e. omitting the common root where my root package is org.wol.useridentityservice
. For example, the src files in the auth0
directory have package org.wol.useridentityservice.auth0
and so on.
Now I get to my test
directory. Should the directory structure match main
so that auth0 tests are in auth0
directy and the package would also be org.wol.useridentityservice.auth0
?Levi
12/20/2023, 6:56 PMmain
and using the same package name, like soLevi
12/20/2023, 6:56 PMTokenIntegrationTest
to distinguish, as you suggestLevi
12/20/2023, 6:56 PMShawn
12/20/2023, 6:56 PMLevi
12/20/2023, 6:56 PMLevi
12/20/2023, 9:14 PMEngineMain
to start a server and in my Application.module()
I set up some dependencies, such as an Auth0 client, kafka consumer, etc.
fun main(args: Array<String>) = io.ktor.server.netty.EngineMain.main(args)
fun Application.module() {
val auth0Client = Auth0Client(environment.config)
val residentCache = KafkaResidentCache()
userRoutes(auth0Client, residentCache)
}
I'd like to write an integration test for my routes without relying on a live kafka broker or making requests to Auth0 (obviously). I was hoping I could have default dependencies that get passed all the way up from main
over to the Application.module()
, but I'm not sure if that's possible. I've seen some examples that seem a bit more flexible (e.g. this example where they have fun Application.main(httpClient: HttpClient = applicationHttpClient)
which makes it easy to swap a client when testing), but still unclear what exactly this would look like in my case.
Is what I'm aiming for even the right goal? Should I instead be aiming to have an embedded Kafka (a la https://kotest.io/docs/extensions/embedded-kafka.html) or running Kafka in Docker so that the integration tests resemble the production scenario as close as is feasible?Levi
12/20/2023, 9:26 PMfun Application.module(auth0Client: Auth0Client = Auth0Client(environment.config),
residentCache: KafkaResidentCache = KafkaResidentCache()) {
Levi
12/20/2023, 9:26 PM