3

I’m currently trying to change some Spring configuration properties in test code (they aren’t static, that’s why). There’s this odd thing when I try to solve my problem with @ContextConfiguration(initializers = [MyTestClass.Initializer::class]).

and in MyTestClass I defined this:

inner class Initializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
    override fun initialize(applicationContext: ConfigurableApplicationContext) {
        val values = TestPropertyValues.of("spring.datasource.url=" + postgresqlContainer.jdbcUrl)
        values.applyTo(applicationContext)
    }
}

(I’m using Testcontainers here... how to get this working might be a separate question, feel free to help me out.) postgresqlContainer is a member of MyTestClass that I want to access. When I run the test I just get an error:

Caused by: java.lang.IllegalArgumentException: No argument provided for a required parameter: instance of fun com.example.MyTestClass.Initializer.<init>(): com.example.MyTestClass.Initializer

Huh, ok, so I kept debugging a bit and I think it’s Spring’s BeanUtils that isn’t able to handle Kotlin inner classes. If I remove the inner keyword from my inner class BeanUtils can create an instance – doesn’t help me of course, since I need access to the property of the outer class.

I wrote a little test to assert my suspicion:

import io.kotlintest.specs.StringSpec
import org.springframework.beans.BeanUtils

class Thing {
    inner class InnerThing {

    }
}

class BeanUtilTest: StringSpec({
    "instantiate inner class" {
        BeanUtils.instantiateClass(Thing.InnerThing::class.java)
        // fails :-(
    }
})

Question: Is there a workaround? How can I override application properties inside my test in Kotlin?

criedel
  • 346
  • 1
  • 8

1 Answers1

1

I just ran into this and, after a long time trying to figure out what was going on, I finally came up with a solution.

You can use a companion object as follows (e.g., for MySql):

@Testcontainers
@ExtendWith(SpringExtension::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(initializers = [ExampleIntegrationTest.Companion.Initializer::class])
class ExampleIntegrationTest {

    companion object {

        @Container
        @JvmField
        val mySqlContainer = KotlinMySqlContainer()

        class Initializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
            override fun initialize(configurableApplicationContext: ConfigurableApplicationContext) {

                TestPropertyValues.of(
                    "spring.datasource.url=" + mySqlContainer.jdbcUrl,
                    "spring.datasource.username=" + mySqlContainer.username,
                    "spring.datasource.password=" + mySqlContainer.password
                ).applyTo(configurableApplicationContext.environment)
            }
        }
    }
    ...
}
Morrison Cole
  • 3,087
  • 1
  • 15
  • 19