Spring 使用 liquibase 启动测试失败
Spring boot testing with liquibase fails
我已经尝试了很长一段时间来找出解决我的问题的方法,但无济于事。
无论如何,我有一堆集成测试(在与标准测试目录并行的非标准目录 testRegression 中)。
这些集成测试使用内存数据库中的 h2。在生产和测试中,我使用 liquibase 来模拟模式演变。
我的属性(在应用程序中-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=
我一直收到的错误是:
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:
那么我该如何解决这个问题呢?我的基本理解是每个测试 class 创建自己的 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)
谢谢
对于我的特殊情况(仅用于内部测试,不用于生产),我有以下内容:
源码
|-- 主要
|-- 测试
|-- 测试回归
解决方法
确定要使用的liquibase版本(我选择了4.0.0,目前是最新的)
创建文件“src/testRegression/java/liquibase/changelog/StandardChangeLogHistoryService.java”
打开原始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.
中新建的文件中
通过更改代码(第 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 table 创建失败可能有正当理由。但是,在我看来,它已经存在不应该成为重大失败的原因。
我目前的观点是,这应该是官方 liquibase 代码库中的 addressed/fixed。
以下 post(s) 有帮助:
https://github.com/liquibase/liquibase-cache/issues/1
我遇到了同样的问题,这似乎是由数据库 table 名称的区分大小写检查引起的。也就是说,table 被创建为 'DATABASECHANGELOG',但 Liquibase 正在检查是否存在 'databasechangelog'。
修复(至少对于 H2 数据库)是在数据库中指定不区分大小写的标识符 URL。例如:
jdbc:h2:mem:~/mydb;CASE_INSENSITIVE_IDENTIFIERS=TRUE
解释: Spring 测试进程启动 Spring 容器的一个或多个实例以进行 运行 测试。如果它认为两个测试的配置完全相同,它将重新使用一个实例,否则它将启动一个新实例。这些实例是共享的,以避免需要为每个测试启动一个全新的 Springboot 应用程序。但是,问题是实例可能会共享一些资源,例如数据库和网络端口。因此,尝试同时启动多个实例可能会发生错误。在这种情况下,测试套件使用同一个数据库启动两个实例,但第二个实例试图重新 运行 整个 Liquibase 设置,因为区分大小写的问题意味着它没有看到 table 已经创建。
我已经尝试了很长一段时间来找出解决我的问题的方法,但无济于事。
无论如何,我有一堆集成测试(在与标准测试目录并行的非标准目录 testRegression 中)。
这些集成测试使用内存数据库中的 h2。在生产和测试中,我使用 liquibase 来模拟模式演变。
我的属性(在应用程序中-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=
我一直收到的错误是:
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:
那么我该如何解决这个问题呢?我的基本理解是每个测试 class 创建自己的 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)
谢谢
对于我的特殊情况(仅用于内部测试,不用于生产),我有以下内容:
源码
|-- 主要
|-- 测试
|-- 测试回归
解决方法
确定要使用的liquibase版本(我选择了4.0.0,目前是最新的)
创建文件“src/testRegression/java/liquibase/changelog/StandardChangeLogHistoryService.java”
打开原始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.
中新建的文件中通过更改代码(第 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 table 创建失败可能有正当理由。但是,在我看来,它已经存在不应该成为重大失败的原因。
我目前的观点是,这应该是官方 liquibase 代码库中的 addressed/fixed。
以下 post(s) 有帮助: https://github.com/liquibase/liquibase-cache/issues/1
我遇到了同样的问题,这似乎是由数据库 table 名称的区分大小写检查引起的。也就是说,table 被创建为 'DATABASECHANGELOG',但 Liquibase 正在检查是否存在 'databasechangelog'。
修复(至少对于 H2 数据库)是在数据库中指定不区分大小写的标识符 URL。例如:
jdbc:h2:mem:~/mydb;CASE_INSENSITIVE_IDENTIFIERS=TRUE
解释: Spring 测试进程启动 Spring 容器的一个或多个实例以进行 运行 测试。如果它认为两个测试的配置完全相同,它将重新使用一个实例,否则它将启动一个新实例。这些实例是共享的,以避免需要为每个测试启动一个全新的 Springboot 应用程序。但是,问题是实例可能会共享一些资源,例如数据库和网络端口。因此,尝试同时启动多个实例可能会发生错误。在这种情况下,测试套件使用同一个数据库启动两个实例,但第二个实例试图重新 运行 整个 Liquibase 设置,因为区分大小写的问题意味着它没有看到 table 已经创建。