Creating Temporary Directory for Unit Test with Spring
Replacing a value in the spring context is possible, but how can we intercept the creation of beans and pass in the replaced value? This requires understanding of how Spring loads its beans during startup and provide a customized way to replace such value.
I explored the following ways to achieve this result
- Build TestExecutionListener. Managed to insert the value to application-test.yaml but it seems AppConfig bean is created before the insertion happens
class MyTestExecutionListener : TestExecutionListener, Ordered {
lateinit var tempDir: File
override fun beforeTestMethod(testContext: TestContext) {
tempDir = Files.createTempDirectory("my-test").toFile()
System.setProperty("test.tempDir", tempDir.toString())
println("Before test method: ${testContext.testMethod.name}")
}
override fun afterTestMethod(testContext: TestContext) {
tempDir.deleteRecursively()
println("After test method: ${testContext.testMethod.name}")
}
override fun getOrder(): Int {
return -1000
}
}
@TestExecutionListeners(MyTestExecutionListener::class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
@SpringBootTest
@ActiveProfiles("test")
class MyTestClass {
@Autowired
lateinit var appConfig: AppConfig
@Test
fun test1() {
println("Running test1")
}
@Test
fun test2() {
println("Running test2")
}
@Test
fun `should replace the files dir path`(@Value("\${files.dir}") dirPath: String) {
Assertions.assertNotNull(dirPath)
Assertions.assertEquals(dirPath, appConfig.filesConfig.dir)
}
}
2. The following example use @TempDir, it has the same result as #1
interestingly, if i move @Value(“\${files.dir}”) dirPath: String into class level, it will inject the default value, meaning that it will ignore my replacement.
@SpringBootTest
@ActiveProfiles("test")
class TempDirReplaceTest {
@Autowired
lateinit var applicationContext: ApplicationContext
@TempDir
lateinit var tempDir: Path
@BeforeEach
fun setUp() {
TestPropertyValues.of(
"test.tempDir=$tempDir"
).applyTo((applicationContext as ConfigurableApplicationContext).environment)
}
@Test
fun `should replace the files dir path`(@Value("\${files.dir}") dirPath: String) {
println(tempDir)
assertNotNull(dirPath)
assertEquals(tempDir.toString(),dirPath.toString())
}
}
3. This is working solution. Log shows the initializer start before springboot.
@SpringBootTest
@ActiveProfiles("test")
@ContextConfiguration(initializers = [BaseIntegrationTest.Companion.TestApplicationContextInitializer::class])
//@TestInstance(TestInstance.Lifecycle.PER_CLASS)
abstract class BaseIntegrationTest {
companion object {
class TestApplicationContextInitializer: ApplicationContextInitializer<ConfigurableApplicationContext> {
override fun initialize(applicationContext: ConfigurableApplicationContext) {
println("TestApplicationContextInitializer initialize")
val tempDir = Files.createTempDirectory("my-test").toFile()
TestPropertyValues.of(
"test.tempDir=$tempDir"
).applyTo(applicationContext.environment)
}
}
@JvmStatic
@AfterAll
fun cleanUp(@Value("\${files.dir}") dirPath: String) {
println("clean up the temp dir")
File(dirPath).deleteRecursively()
println("cleaned temp dir")
}
}
}
internal class ApplicationInitializerExample: BaseIntegrationTest() {
@Autowired
lateinit var appConfig: AppConfig
@Test
fun `should replace the files dir path`(@Value("\${files.dir}") dirPath: String) {
println("Start ApplicationInitializerExample")
assertNotNull(dirPath)
println("appConfig.filesConfig.dir ${appConfig.filesConfig.dir}")
Assertions.assertEquals(appConfig.filesConfig.dir, dirPath)
}
}
Spring-kafka has a way to replace the properties before the actual instance is created? Or maybe it is not using the application-test.yaml to achieve that at all.