使用 liquibase 进行 Spring Boot 测试失败

mat*_*999 5 java h2 liquibase spring-boot

我已经尝试了很长一段时间来为我的问题找出解决方案,但无济于事。

无论如何,我有一堆集成测试(在与标准测试目录平行的非标准目录 testRegression 中)。

这些集成测试在内存数据库中使用 h2。在生产和测试中,我使用 liquibase 来模拟模式演变。

我的属性(在 application-testRegession.properties 中)如下所示:

spring.liquibase.enabled=true
spring.liquibase.user=sa
spring.liquibase.password=
spring.liquibase.change-log=classpath:/liquibase/changelog-master.xml

spring.datasource.url=jdbc:p6spy:h2:mem:testdb;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS nmc\\;CREATE SCHEMA IF NOT EXISTS mkt\\;CREATE SCHEMA IF NOT EXISTS cdb\\;CREATE SCHEMA IF NOT EXISTS pg_temp
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.username=sa
spring.datasource.password=
Run Code Online (Sandbox Code Playgroud)

我一直收到的错误是:

2020-07-21 15:57:34.173 INFO  [liquibase.lockservice.StandardLockService] [Test worker:13]: Successfully acquired change log lock
2020-07-21 15:57:34.303 INFO  [liquibase.changelog.StandardChangeLogHistoryService] [Test worker:13]: Creating database history table with name: PUBLIC.DATABASECHANGELOG
2020-07-21 15:57:34.305 INFO  [liquibase.executor.jvm.JdbcExecutor] [Test worker:13]: CREATE TABLE PUBLIC.DATABASECHANGELOG (ID VARCHAR(255) NOT NULL, AUTHOR VARCHAR(255) NOT NULL, FILENAME VARCHAR(255) NOT NULL, DATEEXECUTED TIMESTAMP NOT NULL, ORDEREXECUTED INT NOT NULL, EXECTYPE VARCHAR(10) NOT NULL, MD5SUM VARCHAR(35), DESCRIPTION VARCHAR(255), COMMENTS VARCHAR(255), TAG VARCHAR(255), LIQUIBASE VARCHAR(20), CONTEXTS VARCHAR(255), LABELS VARCHAR(255), DEPLOYMENT_ID VARCHAR(10))
2020-07-21 15:57:34.307 INFO  [liquibase.lockservice.StandardLockService] [Test worker:13]: Successfully released change log lock
2020-07-21 15:57:34.309 WARN  [org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext] [Test worker:13]: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]: Invocation of init method failed; nested exception is liquibase.exception.DatabaseException: Table "DATABASECHANGELOG" already exists; SQL statement:
CREATE TABLE PUBLIC.DATABASECHANGELOG (ID VARCHAR(255) NOT NULL, AUTHOR VARCHAR(255) NOT NULL, FILENAME VARCHAR(255) NOT NULL, DATEEXECUTED TIMESTAMP NOT NULL, ORDEREXECUTED INT NOT NULL, EXECTYPE VARCHAR(10) NOT NULL, MD5SUM VARCHAR(35), DESCRIPTION VARCHAR(255), COMMENTS VARCHAR(255), TAG VARCHAR(255), LIQUIBASE VARCHAR(20), CONTEXTS VARCHAR(255), LABELS VARCHAR(255), DEPLOYMENT_ID VARCHAR(10)) [42101-197] [Failed SQL: (42101) CREATE TABLE PUBLIC.DATABASECHANGELOG (ID VARCHAR(255) NOT NULL, AUTHOR VARCHAR(255) NOT NULL, FILENAME VARCHAR(255) NOT NULL, DATEEXECUTED TIMESTAMP NOT NULL, ORDEREXECUTED INT NOT NULL, EXECTYPE VARCHAR(10) NOT NULL, MD5SUM VARCHAR(35), DESCRIPTION VARCHAR(255), COMMENTS VARCHAR(255), TAG VARCHAR(255), LIQUIBASE VARCHAR(20), CONTEXTS VARCHAR(255), LABELS VARCHAR(255), DEPLOYMENT_ID VARCHAR(10))]
2020-07-21 15:57:34.309 INFO  [com.zaxxer.hikari.HikariDataSource] [Test worker:13]: HikariPool-3 - Shutdown initiated...
2020-07-21 15:57:34.324 INFO  [com.zaxxer.hikari.HikariDataSource] [Test worker:13]: HikariPool-3 - Shutdown completed.
2020-07-21 15:57:34.326 INFO  [org.apache.catalina.core.StandardService] [Test worker:13]: Stopping service [Tomcat]
2020-07-21 15:57:34.342 INFO  [org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener] [Test worker:13]: 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-07-21 15:57:34.345 ERROR [org.springframework.boot.SpringApplication] [Test worker:13]: Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]: Invocation of init method failed; nested exception is liquibase.exception.DatabaseException: Table "DATABASECHANGELOG" already exists; SQL statement:
Run Code Online (Sandbox Code Playgroud)

那么我该如何解决这个问题呢?我的基本理解是每个测试类都会创建自己的 ApplicationContext。为此,它会创建一个 liquibase bean 并将其加载到其中。但是,此问题仅发生在 42 次测试中的 2 次。

我真的很想深入了解并了解发生了什么。任何人都可以阐明我的问题吗?

另外 ,测试单独运行都很好,但是当作为一个组运行时,它们会失败。

UPDATE 1 相关属性如下:

spring.main.allow-bean-definition-overriding=true
spring.datasource.url=jdbc:p6spy:h2:mem:testdb;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS nmc\\;CREATE SCHEMA IF NOT EXISTS mkt\\;CREATE SCHEMA IF NOT EXISTS cdb\\;CREATE SCHEMA IF NOT EXISTS pg_temp
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.hikari.connectionTimeout=10000
spring.datasource.hikari.idleTimeout=60000
spring.datasource.hikari.maxLifetime=180000
spring.datasource.hikari.maximumPoolSize=50
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
Run Code Online (Sandbox Code Playgroud)

我的配置是:

@Configuration
@ComponentScan(
    basePackages = {
      "com.aareal.nmc"
    },
    excludeFilters = {
      @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = CommandLineRunner.class)
    })
@EnableTransactionManagement
@Profile("testRegression")
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
@EnableConfigurationProperties(LiquibaseProperties.class)
public class RegressionTestConfig {
Run Code Online (Sandbox Code Playgroud)

我的两个测试被注释为:

@RunWith(SpringRunner.class)
@SpringBootTest(
    classes = {
      RegressionTestConfig.class
    },
    //webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
Run Code Online (Sandbox Code Playgroud)

谢谢

Bar*_*W19 6

我有同样的问题,它似乎是由对数据库表名的区分大小写检查引起的。也就是说,该表被创建为“DATABASECHANGELOG”,但 Liquibase 正在检查“databasechangelog”是否存在。

修复(至少对于 H2 数据库)是在数据库 URL 中指定不区分大小写的标识符。例如:

jdbc:h2:mem:~/mydb;CASE_INSENSITIVE_IDENTIFIERS=TRUE
Run Code Online (Sandbox Code Playgroud)

说明: Spring 测试过程会启动一个或多个 Spring 容器实例来运行测试。如果它认为两个测试的配置完全相同,它将重用一个实例,否则它将启动一个新的实例。这些实例是共享的,以避免需要为每个测试启动一个全新的 Springboot 应用程序。但是,问题是实例可能会共享一些资源,例如数据库和网络端口。因此,尝试同时启动多个实例可能会发生错误。在这种情况下,测试套件使用同一个数据库启动两个实例,但第二个实例尝试重新运行整个 Liquibase 设置,因为区分大小写的问题意味着它没有看到已经创建的表。


mat*_*999 2

对于我的特殊情况(即仅用于内部测试,而不是生产),我有以下内容:

src
|-- 主
|-- 测试
|-- 测试回归

解决方法

  1. 决定要使用的 liquibase 版本(我选择了 4.0.0,这是目前最新的版本)

  2. 创建文件“src/testRegression/java/liquibase/changelog/StandardChangeLogHistoryService.java”

  3. 打开原始liquibase文件“StandardChangeLogHistoryService.java”(我的位于~//.gradle/caches/modules-2/files-2.1/org.liquibase/liquibase-core/4.0.0/23a5317eb5005b4765cd85e6f3a2cc4bb55c0daa/liquibase-core-4.0中。我复制并解压了 0-sources.jar)并将其内容 1:1 复制到 2 中新创建的文件中。

  4. 通过更改代码(第 396 行左右)来添加 catch 块

       if (SqlGeneratorFactory.getInstance().supports(sql, database)) {
          executor.execute(sql);
          getDatabase().commit();
       } else {
    
    Run Code Online (Sandbox Code Playgroud)

   if (SqlGeneratorFactory.getInstance().supports(sql, database)) {
    try {
      executor.execute(sql);
      getDatabase().commit();
    } catch (DatabaseException excptn) {
      Scope.getCurrentScope()
          .getLog(getClass())
          .warning(
              "Table '"
                  + getDatabase()
                      .escapeTableName(
                          getLiquibaseCatalogName(),
                          getLiquibaseSchemaName(),
                          getDatabaseChangeLogTableName())
                  + "' already exists.");
    }
  } else {
Run Code Online (Sandbox Code Playgroud)

这只是一种解决方法,因为 ChangeLogTable 表创建失败可能有合理的原因。然而,在我看来,它已经存在不应成为重大失败的原因。

我目前的观点是,这是应该在官方 liquibase 代码库中解决/修复的问题。

以下帖子很有帮助: https://github.com/liquibase/liquibase-cache/issues/1