小编典典

如何在Spring Integration测试之间删除内存中的h2db?

spring-boot

我在Spring Web应用程序中使用Liquibase。我有一堆实体,在针对每个实体(如用户,帐户,发票,许可证等)的集成测试中,有数百种针对REST
API的测试。我的所有集成测试在按类运行时均通过,但当使用gradle test。测试之间很可能存在数据冲突,到目前为止,我对花时间修复清理数据不感兴趣。我更喜欢在每节课之后删除数据库和上下文。我认为我可以@DirtiesContext在课堂上使用,因此我通过测试对其进行了注释。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class, SecurityConfiguration.class},
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext
public class InvoiceResourceIntTest {

我看到添加注释后,Web应用程序上下文对每个类都启动,但是当Liquibase初始化发生时,由于校验和匹配,查询将不会运行。由于这是一个内存中的数据库,因此我希望该数据库与spring上下文一起被销毁,但是这没有发生。

我也将jpa hibernate ddl-auto设置为,create- drop但这没有帮助。我正在考虑的下一个选项是,而不是mem将h2db写入文件,然后在集成测试类文件的@BeforeClass中删除该文件。我更喜欢将db自动删除到内存中,而不是在测试中对其进行管理,但是我想在此尝试一下作为最后的选择。谢谢您的帮助。

更新:

我更新了测试,如下所示。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class, SecurityConfiguration.class},
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
    properties = "spring.datasource.name=AccountResource")
@DirtiesContext
public class AccountResourceIntTest {

我为每个集成测试设置了唯一的名称。我仍然看不到数据库是新数据库,因为在日志中只能看到Liquibase校验和。

这是来自application.yml的我的应用程序配置

spring:
    datasource:
        driver-class-name: org.h2.Driver
        url: jdbc:h2:mem:myApp;DB_CLOSE_DELAY=-1
        name:
        username:
        password:
    jpa:
        database-platform: com.neustar.registry.le.domain.util.FixedH2Dialect
        database: H2
        open-in-view: false
        show_sql: true
        hibernate:
            ddl-auto: create-drop
            naming-strategy: org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
        properties:
            hibernate.cache.use_second_level_cache: false
            hibernate.cache.use_query_cache: false
            hibernate.generate_statistics: true
            hibernate.hbm2ddl.auto: validate

如果重要的话,我的项目是从JHipster
2.x版本生成的。请在下面查看我的数据库配置类。AppProperty是应用程序特定的属性(与Spring不同)。

@Configuration
public class DatabaseConfiguration {

    private static final int LIQUIBASE_POOL_INIT_SIZE = 1;
    private static final int LIQUIBASE_POOL_MAX_ACTIVE = 1;
    private static final int LIQUIBASE_POOL_MAX_IDLE = 0;
    private static final int LIQUIBASE_POOL_MIN_IDLE = 0;

    private static final Logger LOG = LoggerFactory.getLogger(DatabaseConfiguration.class);

    /**
     * Creates data source.
     *
     * @param dataSourceProperties Data source properties configured.
     * @param appProperties the app properties
     * @return Data source.
     */
    @Bean(destroyMethod = "close")
    @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
    @Primary
    public DataSource dataSource(final DataSourceProperties dataSourceProperties,
        final AppProperties appProperties) {

        LOG.info("Configuring Datasource with url: {}, user: {}",
            dataSourceProperties.getUrl(), dataSourceProperties.getUsername());

        if (dataSourceProperties.getUrl() == null) {
            LOG.error("Your Liquibase configuration is incorrect, please specify database URL!");
            throw new ApplicationContextException("Data source is not configured correctly, please specify URL");
        }
        if (dataSourceProperties.getUsername() == null) {
            LOG.error("Your Liquibase configuration is incorrect, please specify database user!");
            throw new ApplicationContextException(
                "Data source is not configured correctly, please specify database user");
        }
        if (dataSourceProperties.getPassword() == null) {
            LOG.error("Your Liquibase configuration is incorrect, please specify database password!");
            throw new ApplicationContextException(
                "Data source is not configured correctly, "
                    + "please specify database password");
        }

        PoolProperties config = new PoolProperties();
        config.setDriverClassName(dataSourceProperties.getDriverClassName());
        config.setUrl(dataSourceProperties.getUrl());
        config.setUsername(dataSourceProperties.getUsername());
        config.setPassword(dataSourceProperties.getPassword());
        config.setInitialSize(appProperties.getDatasource().getInitialSize());
        config.setMaxActive(appProperties.getDatasource().getMaxActive());
        config.setTestOnBorrow(appProperties.getDatasource().isTestOnBorrow());
        config.setValidationQuery(appProperties.getDatasource().getValidationQuery());

        org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource(config);
        LOG.info("Data source is created: {}", dataSource);

        return dataSource;

    }

    /**
     * Create data source for Liquibase using dba user and password provided for "liquibase"
     * in application.yml.
     *
     * @param dataSourceProperties Data source properties
     * @param liquibaseProperties Liquibase properties.
     * @param appProperties the app properties
     * @return Data source for liquibase.
     */
    @Bean(destroyMethod = "close")
    @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
    public DataSource liquibaseDataSource(final DataSourceProperties dataSourceProperties,
        final LiquibaseProperties liquibaseProperties, final AppProperties appProperties) {

        LOG.info("Configuring Liquibase Datasource with url: {}, user: {}",
            dataSourceProperties.getUrl(), liquibaseProperties.getUser());

        /*
         * This is needed for integration testing. When we run integration tests using SpringJUnit4ClassRunner, Spring
         * uses
         * H2DB if it is in the class path. In that case, we have to create pool for H2DB.
         * Need to find a better solution for this.
         */
        if (dataSourceProperties.getDriverClassName() != null
            && dataSourceProperties.getDriverClassName().startsWith("org.h2.")) {
            return dataSource(dataSourceProperties, appProperties);
        }

        if (dataSourceProperties.getUrl() == null) {
            LOG.error("Your Liquibase configuration is incorrect, please specify database URL!");
            throw new ApplicationContextException("Liquibase is not configured correctly, please specify URL");
        }
        if (liquibaseProperties.getUser() == null) {
            LOG.error("Your Liquibase configuration is incorrect, please specify database user!");
            throw new ApplicationContextException(
                "Liquibase is not configured correctly, please specify database user");
        }
        if (liquibaseProperties.getPassword() == null) {
            LOG.error("Your Liquibase configuration is incorrect, please specify database password!");
            throw new ApplicationContextException(
                "Liquibase is not configured correctly, please specify database password");
        }

        PoolProperties config = new PoolProperties();

        config.setDriverClassName(dataSourceProperties.getDriverClassName());
        config.setUrl(dataSourceProperties.getUrl());
        config.setUsername(liquibaseProperties.getUser());
        config.setPassword(liquibaseProperties.getPassword());

        // for liquibase pool, we dont need more than 1 connection
        config.setInitialSize(LIQUIBASE_POOL_INIT_SIZE);
        config.setMaxActive(LIQUIBASE_POOL_MAX_ACTIVE);

        // for liquibase pool, we dont want any connections to linger around
        config.setMaxIdle(LIQUIBASE_POOL_MAX_IDLE);
        config.setMinIdle(LIQUIBASE_POOL_MIN_IDLE);

        org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource(config);
        LOG.info("Liquibase data source is created: {}", dataSource);

        return dataSource;

    }

    /**
     * Creates a liquibase instance.
     *
     * @param dataSource Data source to use for liquibase.
     * @param dataSourceProperties Datasource properties.
     * @param liquibaseProperties Liquibase properties.
     * @return Liquibase instance to be used in spring.
     */
    @Bean
    public SpringLiquibase liquibase(@Qualifier("liquibaseDataSource") final DataSource dataSource,
        final DataSourceProperties dataSourceProperties, final LiquibaseProperties liquibaseProperties) {

        // Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously
        SpringLiquibase liquibase = new AsyncSpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog("classpath:config/liquibase/master.xml");
        liquibase.setContexts(liquibaseProperties.getContexts());
        liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
        liquibase.setDropFirst(liquibaseProperties.isDropFirst());
        liquibase.setShouldRun(liquibaseProperties.isEnabled());

        return liquibase;

    }

}

阅读 311

收藏
2020-05-30

共1个答案

小编典典

这是因为每个测试共享同一个数据库,并且H2的生命周期不在我们的控制范围内。如果您启动一个进程(VM)并需要一个名为的数据库foo,请关闭应用程序上下文,再启动一个新的,然后foo再次要求您将获得相同的实例。

在即将1.4.2发布的版本中,我们添加了一个属性,用于在启动时为数据库生成一个唯一的名称(请参阅参考资料spring.datasource.generate- unique-name),默认情况下,该值将设置为true(在1.5中)。

在此期间,您可以标注每个测试@SpringBootTest(properties="spring.datasource.name=xyz"),其中xyz是需要单独的DB测试不同。

2020-05-30