Local development with Testcontainers, Kotlin and Spring Boot
Thomas Schühly’s server-side rendering journey started as a developer trying to make life easier while developing his first bootstrapped product in his free time. Creating Spring ViewComponent enabled him to be the youngest Speaker at the largest European Spring conference and build awesome software full-time with his open-source library at alanda.io. He regularly talks at Java User Groups about htmx and server-side rendering with Spring while contributing to the open-source community. PhotoQuest
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",
)
}
If you want to learn more about HTMX + Spring Boot check out my series Web development without the JavaScript headache with Spring + HTMX.
My side business PhotoQuest is also built with HTMX + JTE




