我在Spring Web应用程序中使用Liquibase。我有一堆实体,在针对每个实体(如用户,帐户,发票,许可证等)的集成测试中,有数百种针对REST API的测试。我的所有集成测试在按类运行时均通过,但当使用gradle test。测试之间很可能存在数据冲突,到目前为止,我对花时间修复清理数据不感兴趣。我更喜欢在每节课之后删除数据库和上下文。我认为我可以@DirtiesContext在课堂上使用,因此我通过测试对其进行了注释。
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自动删除到内存中,而不是在测试中对其进行管理,但是我想在此尝试一下作为最后的选择。谢谢您的帮助。
create- drop
mem
更新:
我更新了测试,如下所示。
@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; } }
这是因为每个测试共享同一个数据库,并且H2的生命周期不在我们的控制范围内。如果您启动一个进程(VM)并需要一个名为的数据库foo,请关闭应用程序上下文,再启动一个新的,然后foo再次要求您将获得相同的实例。
foo
在即将1.4.2发布的版本中,我们添加了一个属性,用于在启动时为数据库生成一个唯一的名称(请参阅参考资料spring.datasource.generate- unique-name),默认情况下,该值将设置为true(在1.5中)。
1.4.2
spring.datasource.generate- unique-name
在此期间,您可以标注每个测试@SpringBootTest(properties="spring.datasource.name=xyz"),其中xyz是需要单独的DB测试不同。
@SpringBootTest(properties="spring.datasource.name=xyz")
xyz