[英]Test Config Beans Loaded into Spring Context During App Run
I'm working on a spring boot (v 2.2.4) app, specifically to add integration tests which leverage Testcontainers to instantiate a docker container that runs a Postgres instance for tests to perform database transactions against.我正在开发一个 spring 启动(v 2.2.4)应用程序,专门添加集成测试,利用Testcontainers实例化一个 docker 容器,该容器运行 Postgres 实例以进行测试以执行数据库事务。 The tests push our database schema into the Postgres instance via Liquibase .测试通过Liquibase将我们的数据库模式推送到 Postgres 实例中。 I implemented this following this guide .我按照本指南实施了这一点。 The connection to the test time Postgres is managed by a class called TestPostgresConfig.java (See below).与测试时间 Postgres 的连接由名为 TestPostgresConfig.java 的 class 管理(见下文)。 The liquibase operations are performed by a SpringLiquibase object defined in the same class. liquibase 操作由同一个 class 中定义的 SpringLiquibase object 执行。 I run into a problem when I try running the application after successfully building.成功构建后尝试运行应用程序时遇到问题。 The issue is the Spring context tries to instantiate the SpringLiquibase bean at runtime (fails due to db.changelog-master.yaml not being found) and I don't want it to do so:问题是 Spring 上下文尝试在运行时实例化 SpringLiquibase bean(由于 db.changelog-master.yaml 未找到而失败),我不希望它这样做:
WARN [main] org.springframework.context.support.AbstractApplicationContext: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: WARN [main] org.springframework.context.support.AbstractApplicationContext:在上下文初始化期间遇到异常 - 取消刷新尝试:org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'liquibase' defined in class path resource在 class 路径资源中定义名称为“liquibase”的 bean 创建错误
[org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]: Invocation of init method failed; [org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]:init方法调用失败; nested exception is liquibase.exception.ChangeLogParseException: Error parsing classpath:db/changelog/changelog-master.yaml嵌套异常是 liquibase.exception.ChangeLogParseException: Error parsing classpath:db/changelog/changelog-master.yaml
Cause by java.io.FileNotFoundException class path resource [db/changelog/changelog-master.yaml] cannot be resolved to URL because it does not exist Cause by java.io.FileNotFoundException class path resource [db/changelog/changelog-master.yaml] cannot be resolved to URL because it does not exist
This file does not exist, will never exist in this project, and liquibase should not be trying to push change logs at runtime in the first place.这个文件不存在,永远不会存在于这个项目中,并且 liquibase 不应该首先尝试在运行时推送更改日志。 I need help figuring out why Spring tries to load the liquibase bean so I can keep that from happening at runtime.我需要帮助弄清楚为什么 Spring 会尝试加载 liquibase bean,以便我可以防止在运行时发生这种情况。
My set up:我的设置:
@SpringBootApplication
@EnableRetry
@EnableCommonModule
@EnableScheduling
@Slf4j
@EnableConfigurationProperties({
ExternalProperties.class,
ApplicationProperties.class
})
public class MyApplication implements WebMvcConfigurer, CommandLineRunner {
@Autowired
MyService myService;
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
public void run(String... args) throws Exception {
myService.doSomething();
}
}
TestPostgresConfig.java: TestPostgresConfig.java:
@TestConfiguration
@Profile("integration")
public class TestPostgresConfig {
@Bean
public DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUrl(format("jdbc:postgresql://%s:%s/%s", MyIT.psqlContainer.getContainerIpAddress(),
MyIT.psqlContainer.getMappedPort(5432), MyIT.psqlContainer.getDatabaseName()));
ds.setUsername(MyIT.psqlContainer.getUsername());
ds.setPassword(MyIT.psqlContainer.getPassword());
ds.setSchema(MyIT.psqlContainer.getDatabaseName());
return ds;
}
@Bean
public SpringLiquibase springLiquibase(DataSource dataSource) throws SQLException {
tryToCreateSchema(dataSource);
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDropFirst(true);
liquibase.setDataSource(dataSource);
liquibase.setDefaultSchema("the_schema");
// This and all supported liquibase changelog files are copied onto my classpath
// via the maven assembly plugin. The config to do this has been omitted for the
// sake of brevity
// see this URL for how I did it:
// https://blog.sonatype.com/2008/04/how-to-share-resources-across-projects-in-maven/
liquibase.setChangeLog("classpath:/test/location/of/liquibase.changelog-root.yml");
return liquibase;
}
private void tryToCreateSchema(DataSource dataSource) throws SQLException {
String CREATE_SCHEMA_QUERY = "CREATE SCHEMA IF NOT EXISTS test";
dataSource.getConnection().createStatement().execute(CREATE_SCHEMA_QUERY);
}
}
MyIT.java: MyIT.java:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes=CommonConfig.class)
@ActiveProfile("integration")
@Import(TestPostgresConfig.class)
public class MyIT {
@ClassRule
public static PostgreSQLContainer psqlContainer = new PostgreSQLContainer("postgres:13.1")
.withDatabseName("test-database-instance")
.withUsername("divdiff")
.withPassword("theseAreNotTheDroidsForYou123");
@BeforeClass
public static void init() {
System.setProperty("spring.datasource.url", "jdbc:postgresql://"
+ psqlContainer.getHost() + ":"
+ psqlContainer.getMappedPort(5432) + "/"
+ psqlContainer.getDatabaseName()));
System.setProperty("spring.datasource.username", psqlContainer.getUsername());
System.setProperty("spring.datasource.password", psqlContainer.getPassword());
}
@Before
public void setUp() {
// code to set up my test
}
@Test
public void testMyCodeEndToEnd() {
// my test implementation
}
}
MyConfig.java: MyConfig.java:
@Configuration
@ComponentScan(basePackages = "my.code")
@EntityScan("my.code")
@Slf4j
public class MyConfig {
@Bean
public KeyStore keyStore() {
//load keystore and set javax.net.ssl.keystore* properties
}
@Bean
public KeyStore trustStore() {
//load truststore and set javax.net.ssl.truststore* properties
}
@Bean
public RestTemplate restTemplate() {
//Set up and load SSL Context with key and trust store
//Create HTTPClient and connection stuff
//Look at this link for a similar set up
//https://www.baeldung.com/rest-template
}
}
application-integration.yml应用程序集成.yml
spring:
jpa:
properties:
hibernate:
enable_lazy_load_no_trans: true
profiles:
active: default
server:
ssl:
# My key and trust store values
application:
unrelated-app-properties:
# propertie values below
Package structure: Package结构:
app-project/src/main/java/com/my/code/MyApplication.java应用程序项目/src/main/java/com/my/code/MyApplication.java
app-project/src/main/java/com/my/code/service/MyService.java应用程序项目/src/main/java/com/my/code/service/MyService.java
app-project/src/test/java/my/code/OTHER-TEST-CLASSES-LIVE-HERE... app-project/src/test/java/my/code/OTHER-TEST-CLASSES-LIVE-HERE...
app-project/src/test/java/integration/MyIT.java应用程序项目/src/test/java/integration/MyIT.java
app-project/src/test/java/integration/TestPostgresConfig.java应用程序项目/src/test/java/integration/TestPostgresConfig.java
app-project/src/test/resources/application-integration.yml app-project/src/test/resources/application-integration.yml
my-common-project/src/main/java/common/config/MyConfig.java my-common-project/src/main/java/common/config/MyConfig.java
YOUR HELP IS MUCH APPRECIATED:!!非常感谢您的帮助:!! :D :D
I'm an idiot.我是个白痴。 The maven dependency I brought into for my tests was using provided scope instead of test:我为测试引入的 maven 依赖项是使用提供的 scope 而不是测试:
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>project-with-db-changelogs</artifactId>
<version>1.0-SNAPSHOT</version>
<classifier>resources</classifier>
<type>zip</type>
<scope>provided</scope>
</dependency>
When it should have been test scope:什么时候应该测试 scope:
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>project-with-db-changelogs</artifactId>
<version>1.0-SNAPSHOT</version>
<classifier>resources</classifier>
<type>zip</type>
<scope>test</scope>
</dependency>
Per this link , "This is available only in compile-classpath and test-classpath", hence the liquibase code was being run in both my tests and the resulting jar.根据 此链接,“这仅在 compile-classpath 和 test-classpath 中可用”,因此 liquibase 代码正在我的测试和生成的 jar 中运行。 #amateur-hour #业余时间
You can defile liqubase context as test您可以将 liqubase 上下文污染为测试
<changeSet author="name" id="id-of-file" context="test">
and have an application property like: spring.liquibase.contexts=test并具有如下应用程序属性:spring.liquibase.contexts=test
and add a liquibase bean like:并添加一个 liquibase bean,如:
@Value("${spring.liquibase.contexts}") private String liquibaseContexts; @Value("${spring.liquibase.contexts}") 私有字符串 liquibaseContexts;
@Bean
public SpringLiquibase liquibase() {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(localDatabaseDataSource);
liquibase.setShouldRun(liquibaseEnabled);
liquibase.setChangeLog(localDatabaseLiquibaseChangeLog);
liquibase.setContexts(liquibaseContexts);
return liquibase;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.