简体   繁体   English

在应用程序运行期间加载到 Spring 上下文中的测试配置 Bean

[英]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.

相关问题 Spring Boot 测试:为每个测试加载上下文? - Spring boot test: context loaded for every test? spring 是否在上下文创建期间解析惰性 bean 的属性占位符? - Does spring resolve property placeholders for lazy beans during context creation? 在测试期间初始化应用程序上下文,不包括 spring 集成 bean - Initialize application context during testing excluding the spring integration beans 在测试中加载了错误的Spring应用程序上下文 - Wrong spring application context loaded on test Spring 上下文无法在 Spock 测试方法中加载 - Spring context can not be loaded in Spock test method 从Alfresco集成测试上下文访问Spring bean - Access Spring beans from Alfresco integration test context 在春季测试中的Bean接线期间不获取软件包 - Getting package does not exists during wiring of beans in Spring test 春季:在持久性上下文加载之前运行代码 - Spring: run code before a persistence context is loaded 如何在Spring中以编程方式将Java中的bean添加到app-context.xml中? - How to programatically add beans in Java to the app-context.xml in Spring? 春季2至4:Web应用程序上下文中缺少bean - Spring 2 to 4: beans missing from web-app context
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM