Вопрос или проблема
Я протестировал свое приложение на 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. Если у вас возникнут дополнительные вопросы, не стесняйтесь их задавать.