Serge Iroshnikov
Serge Iroshnikov

Reputation: 919

Spring Boot Tests: How to replace DataSource with one managed by Testcontainers

I tested my Spring Boot app using local PGSQL DB. Now I want to modify tests to use PGSQL running in Testcontainers.

I modified my test configuration class to use Testcontainers and to expose data source connecting to the port returned by Testcontainers:

@TestConfiguration
@SpringBootApplication(exclude = { HazelcastAutoConfiguration.class })
@ComponentScan(basePackages = { "rw.gov.dgie.tm" })
@EntityScan("rw.gov.dgie.tm")
@EnableJpaRepositories(basePackages = { "rw.gov.dgie.tm" })
@EnableAsync(proxyTargetClass = true)
@EnableTransactionManagement
@Testcontainers
public class DbTestConfig {
@Autowired
private DataSource dataSource;

@SuppressWarnings("resource")
@Container
    public static ComposeContainer dockerComposeContainer =
        new ComposeContainer(new File("../docker/catalog-test.yml"))
                .withExposedService("db-catalog", 5432)
                .withRemoveVolumes(false)
                .withLocalCompose(true)
                .waitingFor("db-catalog", new DockerHealthcheckWaitStrategy());

@Bean
    public DataSource dataSource(){
    DriverManagerDataSource source = new DriverManagerDataSource();
    String url = "jdbc:postgresql://" + dockerComposeContainer.getServiceHost("db-catalog", 5432) + ":" + dockerComposeContainer.getServicePort("db-catalog", 5432) + "/db-catalog";
    source.setDriverClassName("org.postgresql.Driver");
    source.setUrl(url);
    source.setUsername("sa");
    source.setPassword("sa");
    return source;
}

}

I am using the following application-test.yml file:

  datasource:
      type: com.zaxxer.hikari.HikariDataSource
      driver-class-name: org.postgresql.Driver
      url: jdbc:postgresql://localhost:5432/db_catalog
      username: db_catalog
      password: qwerty
      hikari:
        poolName: Hikari
        auto-commit: false
    jpa:
      show-sql: true
      generate-ddl: true
      properties:
        hibernate:
          dialect: org.hibernate.dialect.PostgreSQLDialect
          ddl-auto: update
          generate_statistics: false

However, when running test which was run OK with local DB I get the following exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Failed to initialize dependency 'dataSourceScriptDatabaseInitializer' of LoadTimeWeaverAware bean 'entityManagerFactory': Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Unsatisfied dependency expressed through method 'dataSourceScriptDatabaseInitializer' parameter 0: Error creating bean with name 'dbTestConfig': Unsatisfied dependency expressed through field 'dataSource': Error creating bean with name 'dataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?

How can I manage that?

Upvotes: 0

Views: 91

Answers (1)

Parthiv Pradhan
Parthiv Pradhan

Reputation: 1

Few things:

  • TestContainers should be used only for testing, hence move them to the test class folders.
  • Remove @Autowired private DataSource dataSource from
    DbTestConfig. The class is responsible for creating the datasource bean and before creating the bean, the program tries to autowire it. Hence, the error:

Error creating bean with name 'dbTestConfig': Unsatisfied dependency expressed through field 'dataSource': Error creating bean with name 'dataSource': Requested bean is currently in creation:

  • As an alternative, you should use PostgreSQLContainer

Here are the maven dependency

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.19.8</version>
    <scope>test</scope>
</dependency>

Code for instantiating the postgres container:

PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:14.1");
    postgreSQLContainer.withDatabaseName("postgres")
        .withUsername("postgres")
        .withPassword("postgres");
    postgreSQLContainer.start();

The constructor takes the docker image name. As a prerequisite you must have docker desktop installed on windows.

  • Later you can create your datasource bean where you can retrieve the database properties from the postgreSQLContainer object:

     public DataSource datasource() {
    
     DriverManagerDataSource dataSource = new DriverManagerDataSource();
     dataSource.setDriverClassName(postgreSQLContainer.getDriverClassName());
     dataSource.setUrl(postgreSQLContainer.getJdbcUrl());
     dataSource.setUsername(postgreSQLContainer.getUsername());
     dataSource.setPassword(postgreSQLContainer.getPassword());
     return dataSource;
    

}

please refer to a sample test configuration

For more details refer

Upvotes: 0

Related Questions