繁体   English   中英

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

[英]Test Config Beans Loaded into Spring Context During App Run

我正在开发一个 spring 启动(v 2.2.4)应用程序,专门添加集成测试,利用Testcontainers实例化一个 docker 容器,该容器运行 Postgres 实例以进行测试以执行数据库事务。 测试通过Liquibase将我们的数据库模式推送到 Postgres 实例中。 我按照本指南实施了这一点。 与测试时间 Postgres 的连接由名为 TestPostgresConfig.java 的 class 管理(见下文)。 liquibase 操作由同一个 class 中定义的 SpringLiquibase object 执行。 成功构建后尝试运行应用程序时遇到问题。 问题是 Spring 上下文尝试在运行时实例化 SpringLiquibase bean(由于 db.changelog-master.yaml 未找到而失败),我不希望它这样做:

WARN [main] org.springframework.context.support.AbstractApplicationContext:在上下文初始化期间遇到异常 - 取消刷新尝试:org.springframework.beans.factory.BeanCreationException:

在 class 路径资源中定义名称为“liquibase”的 bean 创建错误

[org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]:init方法调用失败; 嵌套异常是 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

这个文件不存在,永远不会存在于这个项目中,并且 liquibase 不应该首先尝试在运行时推送更改日志。 我需要帮助弄清楚为什么 Spring 会尝试加载 liquibase bean,以便我可以防止在运行时发生这种情况。

我的设置:

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

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

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

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

应用程序集成.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结构:

应用程序项目/src/main/java/com/my/code/MyApplication.java

应用程序项目/src/main/java/com/my/code/service/MyService.java

app-project/src/test/java/my/code/OTHER-TEST-CLASSES-LIVE-HERE...

应用程序项目/src/test/java/integration/MyIT.java

应用程序项目/src/test/java/integration/TestPostgresConfig.java

app-project/src/test/resources/application-integration.yml

my-common-project/src/main/java/common/config/MyConfig.java

非常感谢您的帮助:!! :D

我是个白痴。 我为测试引入的 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>

什么时候应该测试 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>

根据 此链接,“这仅在 compile-classpath 和 test-classpath 中可用”,因此 liquibase 代码正在我的测试和生成的 jar 中运行。 #业余时间

您可以将 liqubase 上下文污染为测试

<changeSet author="name" id="id-of-file" context="test">

并具有如下应用程序属性:spring.liquibase.contexts=test

并添加一个 liquibase bean,如:

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM