Local development with Testcontainers, Kotlin and Spring Boot
It is always the best to keep your development environment as close as you can to your production environment. Don’t use a h2 database as it can behave differently to a production database: Issues mapping @Lob
Recently I found the article Local development with Testcontainers by Sergei Egorov @bsideup.
I couldn’t get it working with Kotlin, but after some help from the awesome twitter developer community I got it working.
Refactor your Application.kt, so you expose createSpringApplication as a static method:
@SpringBootApplication
class ExampleApplication{
companion object {
@JvmStatic
fun createSpringApplication(): SpringApplication {
return SpringApplication(ExampleApplication::class.java)
}
}
}
fun main(args: Array<String>) {
runApplication<ExampleApplication>(*args)
}
Create a DevelopmentInitializer.kt in src/test/kotlin/com.example/DevelopmentInitializer.kt
@RunWith(SpringRunner::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(initializers = [DevelopmentInitializer.Initializer::class])
abstract class DevelopmentInitializer {
class Initializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
override fun initialize(context: ConfigurableApplicationContext) {
val env = context.environment
env.propertySources.addFirst(
MapPropertySource(
"testcontainers", properties
)
)
}
companion object {
private val postgresContainer = PostgreSQLContainer("postgres:14.4")
.withUsername("postgres")
.withPassword("password")
.withDatabaseName("postgres")
.withExposedPorts(5432)
.withReuse(true)
val properties: Map<String, String>
get() {
Startables.deepStart(postgresContainer).join()
return mapOf(
"spring.datasource.url" to postgresContainer.jdbcUrl,
"spring.datasource.password" to postgresContainer.password,
"spring.datasource.username" to postgresContainer.username,
)
}
}
}
}
To create reusable containers you need to set testcontainers.reuse.enable=true
in $HOME/.testcontainers.properties
file
Now create a DevelopmentApplication.kt in src/test/kotlin/com.example/DevelopmentApplication.kt
fun main(args: Array<String>) {
val app = ExampleApplication.createSpringApplication()
app.addInitializers(DevelopmentInitializer.Initializer())
app.run(*args)
}
You can start DevelopmentApplication.main() to get your Spring Boot Application with an autoconfigured postgres container.
I created a working example at my GitHub: github.com/tschuehly/testcontainers-localdev-kotlin
Fixed Ports⌗
You can also use a FixedHostPortContainer, then you can connect for example your IntelliJ Database Explorer and don’t have to reconfigure it if you restart your Docker Environment
val postgresContainer = FixedHostPortGenericContainer("postgres:14.4")
.withEnv("POSTGRES_USER","postgres")
.withEnv("POSTGRES_PASSWORD","password")
.withEnv("POSTGRES_DB","postgres")
.withFixedExposedPort(5432,5432)
.withReuse(true)
val properties: Map<String, String>
get(){
Startables.deepStart(postgresContainer, minioContainer).join()
return mapOf(
"spring.datasource.url" to
"jdbc:postgresql://" + postgresContainer.host + ":5432/postgres",
"spring.datasource.password" to "password",
"spring.datasource.username" to "postgres",
)
}