Тесты Spring Boot: Как заменить DataSource на управляемый Testcontainers

Вопрос или проблема

Я протестировал свое приложение на Spring Boot, используя локальную базу данных PGSQL. Теперь я хочу изменить тесты, чтобы они использовали PGSQL, работающий в Testcontainers.

Я изменил свой класс конфигурации тестов, чтобы использовать Testcontainers и выставлять источник данных, подключающийся к порту, возвращаемому 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;
}

}

Я использую следующий файл application-test.yml:

  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

Однако при запуске теста, который ранее работал с локальной базой данных, я получаю следующее исключение:

org.springframework.beans.factory.BeanCreationException: Ошибка создания бина с именем ‘entityManagerFactory’, определенного в классе ресурсах [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Не удалось инициализировать зависимость ‘dataSourceScriptDatabaseInitializer’ для бина ‘entityManagerFactory’: Ошибка создания бина с именем ‘dataSourceScriptDatabaseInitializer’, определенного в классе ресурсах [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Неудовлетворенная зависимость, выраженная через параметр метода ‘dataSourceScriptDatabaseInitializer’ 0: Ошибка создания бина с именем ‘dbTestConfig’: Неудовлетворенная зависимость, выраженная через поле ‘dataSource’: Ошибка создания бина с именем ‘dataSource’: Запрашиваемый бин в настоящее время создается: Есть ли неразрешимая циклическая ссылка?

Как я могу это решить?

Ответ или решение

Чтобы заменить DataSource, управляемый локальной базой данных PostgreSQL, на DataSource, управляемый Testcontainers в вашем Spring Boot приложении, необходимо внести несколько изменений в конфигурацию классов и настроить правильное использование Testcontainers. Похоже, что вы столкнулись с проблемой круговой зависимости в конфигурации ваших тестов. Мы рассмотрим, как это исправить.

1. Измените конфигурацию тестов

В вашем классе DbTestConfig вы явно указываете @Autowired на DataSource. Это может вызвать проблемы с помещением этого бина в контекст Spring, поскольку бин DataSource еще не создан, когда Spring пытается инжектировать его. Вместо этого, используйте только метод создания бина без автоподставки, чтобы избежать конфликтов.

Вот пример подходящей конфигурации:

@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 {

    @Container
    public static PostgreSQLContainer<?> postgresContainer = 
        new PostgreSQLContainer<>("postgres:latest")
            .withDatabaseName("db_catalog")
            .withUsername("sa")
            .withPassword("sa")
            .withReuse(true); // Для предотвращения повторного создания контейнера

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource source = new DriverManagerDataSource();
        String url = String.format("jdbc:postgresql://%s:%d/db_catalog", 
            postgresContainer.getHost(), 
            postgresContainer.getFirstMappedPort()
        );
        source.setDriverClassName("org.postgresql.Driver");
        source.setUrl(url);
        source.setUsername(postgresContainer.getUsername());
        source.setPassword(postgresContainer.getPassword());
        return source;
    }
}

2. Убедитесь, что зависимости Testcontainers установлены

Добавьте зависимости Testcontainers в ваш pom.xml или build.gradle, если вы их не добавили ранее. Вот пример для Maven:

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>1.16.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.16.0</version>
    <scope>test</scope>
</dependency>

3. Проверьте файл конфигурации application-test.yml

Убедитесь, что файл application-test.yml не конфликтует с конфигурацией, определенной в DbTestConfig. Поскольку мы создаем DataSource вручную, вы можете исключить конфигурацию источника данных из файла:

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/db_catalog # этот URL не будет использоваться
    username: sa
    password: sa
  jpa:
    show-sql: true
    generate-ddl: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect
        ddl-auto: update
        generate_statistics: false

4. Запуск тестов

Теперь вы можете запустить ваши тесты, и они должны работать, используя PostgreSQL, который управляется Testcontainers. Обратите внимание, что Testcontainers автоматически запускает контейнер до выполнения тестов и останавливает его после.

5. Заключение

С помощью этих изменений вы сможете избежать круговых зависимостей и правильно интегрировать Testcontainers с вашим проектом на Spring Boot. Если у вас возникнут дополнительные вопросы, не стесняйтесь их задавать.

Оцените материал
Добавить комментарий

Капча загружается...