当我使用 liquibase 更改模型数据时,JHipster 集成测试失败

Posted

技术标签:

【中文标题】当我使用 liquibase 更改模型数据时,JHipster 集成测试失败【英文标题】:JHipster integration tests fail when I used liquibase to change model data 【发布时间】:2017-09-15 10:39:38 【问题描述】:

总结:我更新了我的课程、测试课程和我的数据库以使用电子邮件登录。我写了一个 liquibase 的更改日志来进行数据库的更改。 运行该项目,一切正常,但如果我执行集成测试,liquibase 的变更日志运行不佳。

我认为正在发生的事情:

    我尝试执行测试。 H2 数据库使用每个变更日志(初始和自定义)进行初始化 开始执行测试。 在每次测试之前,不会删除 H2 数据库,但会调用更改日志(或仅调用我的自定义更改日志)。 我的自定义更改日志失败,因为它找不到在第 2 点删除的登录列,并且最初没有“恢复” 更新日志。

完整版:我用 4.8.0 生成了一个 JHipster 应用程序。我的 .yo-rc.json:


  "generator-jhipster": 
    "promptValues": 
      "packageName": "com.company.name",
      "nativeLanguage": "es"
    ,
    "jhipsterVersion": "4.8.0",
    "baseName": "name",
    "packageName": "com.company.name",
    "packageFolder": "com/company/name",
    "serverPort": "8080",
    "authenticationType": "jwt",
    "hibernateCache": "no",
    "clusteredHttpSession": false,
    "websocket": false,
    "databaseType": "sql",
    "devDatabaseType": "postgresql",
    "prodDatabaseType": "postgresql",
    "searchEngine": false,
    "messageBroker": false,
    "serviceDiscoveryType": false,
    "buildTool": "gradle",
    "enableSocialSignIn": false,
    "enableSwaggerCodegen": false,
    "jwtSecretKey": "secret",
    "clientFramework": "angularX",
    "useSass": true,
    "clientPackageManager": "yarn",
    "applicationType": "monolith",
    "testFrameworks": [],
    "jhiPrefix": "jhi",
    "enableTranslation": true,
    "nativeLanguage": "es",
    "languages": [
      "es"
    ]
  

我想从 JHI_USER 中删除登录列并从 JHI_USER 的电子邮件中删除唯一约束并使其不为空,然后,我进行了我需要的所有代码更改(我删除了对登录的所有引用并将它们更改为电子邮件)并编码三个 liquibase 变更日志的文件:

a) 20170913130000_drop_login_column_JhiUser.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd
                        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
    <changeSet id="20170913130000" author="svalero">
        <dropColumn columnName="login" tableName="jhi_user" />
    </changeSet>
</databaseChangeLog>

B) 20170913180000_drop_unique_constraint_email_JhiUser.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd
                        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
    <changeSet id="20170913180000" author="svalero">
        <dropUniqueConstraint constraintName="jhi_user_email_key"
            tableName="jhi_user" />
    </changeSet>
</databaseChangeLog>

C) 20170913182100_add_notnull_constraint_email_JhiUser.xml

<?xml version="1.0" encoding="UTF-8" ?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd
                        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
    <changeSet id="20170913182100" author="svalero">
        <addNotNullConstraint columnDataType="varchar(100)"
            columnName="email"
            tableName="jhi_user"/>
    </changeSet>
</databaseChangeLog>

我还在 master.xml 中添加了它们,并且在应用程序上一切正常(./gradlew 和 yarn start)。

但是,当我尝试执行集成测试(./gradlew test)时,我在每个测试中都得到了这个:

com.company.name.web.rest.AuditResourceIntTest > getNonExistingAudit FAILED
    java.lang.IllegalStateException
        Caused by: org.springframework.beans.factory.BeanCreationException
            Caused by: liquibase.exception.MigrationFailedException
                Caused by: liquibase.exception.DatabaseException
                    Caused by: org.h2.jdbc.JdbcSQLException

从测试结果中我发现:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [com/company/name/config/DatabaseConfiguration.class]: Invocation of init method failed; nested exception is liquibase.exception.MigrationFailedException: Migration failed for change set config/liquibase/changelog/20170913180000_drop_unique_constraint_email_JhiUser.xml::20170913180000::svalero:
     Reason: liquibase.exception.DatabaseException: Constraint &quot;JHI_USER_EMAIL_KEY&quot; not found; 
     SQL statement: ALTER TABLE PUBLIC.jhi_user DROP CONSTRAINT jhi_user_email_key [90057-196] [Failed SQL: ALTER TABLE PUBLIC.jhi_user DROP CONSTRAINT jhi_user_email_key]

然后,我认为在每次测试中都会调用我的自定义更改日志,但未调用初始更改日志,因为我的更改日志找不到初始更改日志必须创建的列或约束。

我的第一次“绕行”很简单,在变更日志中添加先决条件,例如:

<preConditions onFail="MARK_RAN" onError="MARK_RAN">
    <columnExists columnName="login" tableName="jhi_user" schemaName="public" />
</preConditions>

但 Liquibase 没有“唯一约束”的先决条件,我发现这是为了检查“jhi_user_email_key”约束是否存在:

<preConditions onFail="MARK_RAN" onError="MARK_RAN">
    <sqlCheck expectedResult="1">select count (*) from pg_constraint where conname='jhi_user_email_key'</sqlCheck>
</preConditions>

但是在 H2 中(JHipster 使用这种数据库进行测试)我们没有 pg_constraint 并且这不起作用。

最后,怎么了?为什么测试只运行我的变更日志而忽略初始变更日志?我该如何解决?

【问题讨论】:

【参考方案1】:

与此同时,我在 JHipster 上创建了一个 PR 6460,它被合并了,我猜它已经成为下一个版本 4.9.1 的一部分。唯一约束的名称是 ux_user_mail,即您的文件 B)20170913180000_drop_unique_constraint_email_JhiUser.xml 应更改为:

..... <dropUniqueConstraint constraintName="ux_user_mail" tableName="jhi_user" /> .....

不过,删除 LOGIN 列将对登录过程产生影响,即您需要更改 UserService 类的方法 getUserWithAuthorities,例如

@Transactional(readOnly = true)
public User getUserWithAuthorities() 
    return userRepository.findOneWithAuthoritiesByEmail(SecurityUtils.getCurrentUserLogin()).orElse(null);

loadUserByUsername 在 DomainUserDetailsS​​ervice 类中:

 @Override
@Transactional
public UserDetails loadUserByUsername(final String login) 
    log.debug("Authenticating ", login);
    String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
    Optional<User> userFromDatabase = userRepository.findOneWithAuthoritiesByEmail(lowercaseLogin);
    return ........

UserRepository 也需要通过搜索方法来丰富,例如

@EntityGraph(attributePaths = "authorities")
Optional<User> findOneWithAuthoritiesByEmail(String email);

另一方面,您还需要删除域类 User 的登录属性。仅删除 @Column 注释是不够的。即使属性 login 在没有 @Column 注释的类中,hibernate 也会抛出 SchemaManagementException,因为 JHipster 命名策略是这样配置的

jpa:
    hibernate:
        naming:
            physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
            implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy

并且将期望列登录存在于 jhi_user 表中。命名策略的文档可以在here找到。

对于您的第二个 liquibase changeSet * 20170913180000_drop_unique_constraint_email_JhiUser.xml* 我建议您验证约束 jhi_user_email_key 是否存在。

这将使您可以使用电子邮件登录。仍然通过从域类 User 中删除 login 属性,您将需要从生产和测试代码中重构一些类

【讨论】:

感谢您的回答,但这不是我的问题。当我在我的问题上写下“我做了一些代码更改”时,我的意思是我做了你所说的更改。但是测试失败了,因为我的 liquibase 自定义更改日志在每个测试中都被执行,但初始更改日志没有被执行。 我只是在我的回复中添加了更多细节。作为建议,最好将诸如“一些代码更改”之类的“小细节”放在一起,以便答案更准确。 首先,恐怕我的问题没有正确表达。我不是英语母语人士。我已经完成/编辑了我的请求以尝试指定我的问题。关于您添加的详细信息,谢谢,但这些对我没有帮助。我删除或更改了对“登录”的所有引用。另外,关于“验证约束是否存在。”,如果你的意思是我必须添加一个先决条件来检查这个,我不能这样做,因为 liquibase 没有这样做的先决条件。如果您的意思是我需要检查它是否已创建,是的,初始更改日志会创建它,但我使用自定义更改日志(开发数据库)将其删除。【参考方案2】:

在 JHipster 4.10.0 或更高版本上,感谢@duderoot,它是这样创建的:

        <column name="email" type="varchar(100)">
            <constraints unique="true" nullable="true" uniqueConstraintName="ux_user_email"/>
        </column>       

然后,您可以使用此名称删除此约束,而不会在任何环境中出现问题。


在 JHipster 4.8.0 上,“唯一约束”是这样创建的:

        <column name="email" type="varchar(100)">
            <constraints unique="true" nullable="true"/>
        </column>

它们不使用“uniqueConstraintName”,并且每个数据库上的约束名称都不同。

在 Postgresql(开发配置文件)上,“jhi_user_email_key”存在,但在 H2(测试配置文件)上不存在。

要解决这个问题,您可以在 Initial Schema 上添加 uniqueConstraintName:

        <column name="email" type="varchar(100)">
            <constraints unique="true" nullable="true" uniqueConstraintName="jhi_user_email_key"/>
        </column>

或者使用“dbms”前置条件制作不同的变更日志。

【讨论】:

以上是关于当我使用 liquibase 更改模型数据时,JHipster 集成测试失败的主要内容,如果未能解决你的问题,请参考以下文章

Liquibase - 校验和因不同的数据库供应商而异

从 MySQL 中的现有表生成更改日志时,liquibase 如何处理自动递增的 PK?

无法与 liquibase gradle 插件生成差异

Liquibase 和 Spring 如何使用单独的用户进行架构更改

如何使用 liquibase 更改架构名称

使用 oracle 更改 Liquibase 中的序列