【问题标题】:Spring boot testing with liquibase fails使用 liquibase 进行 Spring Boot 测试失败
【发布时间】:2020-11-12 02:52:13
【问题描述】:

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

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

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

我的属性(在 application-testRegression.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=

我经常遇到的错误是:

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:

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

我真的很想深入了解并了解发生了什么。 谁能解释我的问题?

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

更新 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

我的配置是:

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

我的两个测试注释为:

@RunWith(SpringRunner.class)
@SpringBootTest(
    classes = {
      RegressionTestConfig.class
    },
    //webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

谢谢

【问题讨论】:

  • 当使用并发测试时,他们可能会使用相同的数据库,同样使用@DirtiesContext 可能会尝试重新运行 ligquibase
  • 我没有在测试中的任何地方使用@DirtiesContext。此外,至少据我所知,测试没有同时运行(所有测试都在线程工作者:13 上)。
  • 没有看到测试和配置是不可能回答这个问题的。一定是有什么东西干扰了数据库(通常情况下会加载一次,除非配置不同,并且 H2 在同一虚拟机中可能会出现问题,因为它可能会重用现有的虚拟机)。

标签: java spring-boot h2 liquibase


【解决方案1】:

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

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

jdbc:h2:mem:~/mydb;CASE_INSENSITIVE_IDENTIFIERS=TRUE

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

【讨论】:

  • 找了2个小时终于找到了,谢谢!
  • @BarrySW19:感谢您的回复(我回复有点晚了)。我不确定您要解决的问题是我描述的问题(或者我不明白)。我的问题是数据库表“DATABASECHANGELOG”已经存在。数据库在 2 个测试之间共享,每个测试都尝试创建表。我同意你的解释,但我不确定如何使用“CASE_INSENSITIVE_IDENTIFIERS=TRUE”来解决这个问题?
  • @matze999 - Liquibase 应该检查表是否已经存在,如果不存在则只创建它。可能还有其他原因它看不到表已经存在,但区分大小写问题是我的问题。
【解决方案2】:

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


|-- 主要
|-- 测试
|-- 测试回归

解决方法

  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 {
    

   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 {

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

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

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

【讨论】:

    猜你喜欢
    • 2022-01-05
    • 1970-01-01
    • 2016-09-07
    • 1970-01-01
    • 2018-08-29
    • 2021-09-12
    • 1970-01-01
    • 1970-01-01
    • 2019-12-26
    相关资源
    最近更新 更多