Home > Net >  @DynamicPropertySource not being invoked (Kotlin, Spring Boot and TestContainers)
@DynamicPropertySource not being invoked (Kotlin, Spring Boot and TestContainers)

Time:10-19

I'm trying to define a @TestConfiguration class that is executed once before all integration tests to run a MongoDB TestContainer in Kotlin in a Spring Boot project.

Here is the code:

import org.springframework.boot.test.context.TestConfiguration
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.containers.MongoDBContainer
import org.testcontainers.utility.DockerImageName


@TestConfiguration
class TestContainerMongoConfig {

  companion object {

    @JvmStatic
    private val MONGO_CONTAINER: MongoDBContainer = MongoDBContainer(DockerImageName.parse("mongo").withTag("latest")).withReuse(true)

    @JvmStatic
    @DynamicPropertySource
    private fun emulatorProperties(registry: DynamicPropertyRegistry) {
      registry.add("spring.data.mongodb.uri", MONGO_CONTAINER::getReplicaSetUrl)
    }

    init { MONGO_CONTAINER.start() }

  }

}

The issue seems to be that emulatorProperties method is not being called. The regular flow should be that the container is started and then the properties are set. The first step happens, the second does not.

I know there is an alternative for which I can do this configuration in each functional test class but I don't like it as it adds not needed noise to the test class.

For example, with a Java project that uses Postgres I managed to make it work with the following code:

import javax.sql.DataSource;

import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.utility.DockerImageName;


@TestConfiguration
public class PostgresqlTestContainersConfig {

  static final PostgreSQLContainer POSTGRES_CONTAINER;
  private final static DockerImageName IMAGE = DockerImageName.parse("postgres").withTag("latest");

  static {
    POSTGRES_CONTAINER = new PostgreSQLContainer(IMAGE);
    POSTGRES_CONTAINER.start();
  }


  @Bean
  DataSource dataSource() {
    return DataSourceBuilder.create()
        .username(POSTGRES_CONTAINER.getUsername())
        .password(POSTGRES_CONTAINER.getPassword())
        .driverClassName(POSTGRES_CONTAINER.getDriverClassName())
        .url(POSTGRES_CONTAINER.getJdbcUrl())
        .build();
  }
}

I'm trying to achieve the same thing but in Kotlin and using MongoDB.

Any idea on what may be the issue causing the @DynamicPropertySource not being called?

CodePudding user response:

@DynamicPropertySource is part of the Spring-Boot context lifecycle. Since you want to replicate the Java setup in a way, it is not required to use @DynamicPropertySource. Instead you can follow the Singleton Container Pattern, and replicate it in Kotlin as well.

Instead of setting the config on the registry, you can set them as a System property and Spring Autoconfig will pick it up:

    init { 
      MONGO_CONTAINER.start() 
      System.setProperty("spring.data.mongodb.uri", MONGO_CONTAINER.getReplicaSetUrl());
    }

CodePudding user response:

I was able to resolve similar problem in Groovy by:

Having static method annotated with @DynamicPropetySource directly in the test class (probably it would also work in superclass.

But I didn't want to copy the code into every test class that needs MongoDB. I resolved the issue by using ApplicationContexInitializer

The example is written in groovy

class MongoTestContainer implements ApplicationContextInitializer<ConfigurableApplicationContext>{

  static final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:6.0.2"))

  @Override
  void initialize(ConfigurableApplicationContext applicationContext) {
    mongoDBContainer.start()
    def testValues = TestPropertyValues.of("spring.data.mongodb.uri="  mongoDBContainer.getReplicaSetUrl())
    testValues.applyTo(applicationContext.getEnvironment())
  }
}

To make it complete, in the test class, you just need to add @ContextConfiguration(initializers = MongoTestContainer) to activate context initializer for the test.

For this you could also create custom annotation which would combine @DataMongoTest with previous annotation.

  • Related