持续集成:使测试数据库架构保持最新

时间:2009-01-20 14:43:51

标签: java unit-testing continuous-integration hudson

我正在建立一个持续集成服务器(Hudson)来构建Java项目并运行相关的单元/集成测试。大多数这些测试访问数据库,测试数据保存在DbUnit XML文件中。

我正在寻找一种方法来自动保持测试数据库架构的最新状态。目前,特定版本的SQL脚本存储在以发行版本命名的目录中:

└───scripts
    ├───0.1.0
    ├───0.1.1
    ├───0.1.2
    ├───0.1.4

例如,版本0.1.4的SQL脚本是

scripts\0.1.4\script-0.1.4.sql

问题是这些脚本包含架构更改的混合(例如ALTER TABLE ...)和对静态表的更改(例如,向USER_TYPE表添加新角色)。

在单元测试的情况下,我只想应用模式更改,因为如上所述,单元测试的所有数据都保存在DbUnit XML文件中。虽然我可以将这两种类型的数据库更改分成不同的文件,但是在将版本应用于QA,生产等时,模式更改和数据更改之间通常需要以某种方式强制执行。

无论如何,这只是一个非常冗长的方式,询问是否有人提出了一种强大的方法来自动保持他们的测试架构是最新的?我知道Unitils有一些支持让测试架构保持最新,但我不确定它是否可以“忽略”SQL delta脚本中的数据更新语句。

6 个答案:

答案 0 :(得分:4)

之前的海报将Liquibase列为选项,但是他们没有提到Liquibase定义在特定情境中运行的规则的能力(Contexts in Liquibase)。这允许您使用任何特定上下文标记的模式更新以及标记为test的上下文的单元测试的fixture。这样,只有在运行单元测试时才会插入灯具。

以下是包含架构和灯具的Liquibase更改集的示例:

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.9 http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd">
  <changeSet author="avalade" id="1">
    <createTable tableName="users">
      <column autoIncrement="true" name="id" type="long">
        <constraints nullable="false" primaryKey="true" />
      </column>
      <column name="email" type="varchar(255)" />
    </createTable>
  </changeSet>
  <changeSet author="avalade" id="2" context="test">
    <insert tableName="user">
      <column name="id" value="1" />
      <column name="email" value="test@test.com" />
    </insert>
  </changeSet>
</databaseChangeLog>

然后,如果您使用Spring来管理DAO,可以将以下内容放在您正在部署的应用程序上下文文件中:

<bean id="liquibase" class="liquibase.spring.SpringLiquibase">
  <property name="dataSource" ref="dataSource" />
  <property name="changeLog" value="classpath:dbChangelog.xml" />
</bean>

对于您在单元测试中使用的Application Context文件,请使用其他上下文属性配置Liquibase:

<bean id="liquibase" class="liquibase.spring.SpringLiquibase">
  <property name="dataSource" ref="dataSource" />
  <property name="changeLog" value="classpath:dbChangelog.xml" />
  <property name="contexts" value="test" />
</bean>

这样,您可以将所有数据库定义放在一个位置,只在运行测试代码时插入灯具。

答案 1 :(得分:3)

我们发现管理实时/测试数据库模式逐步演变的最易管理的方法是使用模式迁移管理工具,如Liquibase

这使我们能够以一致的方式将最新的架构更改应用于我们选择,测试或其他任何环境,然后允许我们根据最新架构运行我们希望的任何类型的自动化。

答案 2 :(得分:2)

我在测试中做了什么:

  • 我在某个地方保留了数据库版本
  • 在第一次测试中,我拆掉整个数据库并从头开始构建
  • 我在单个测试中运行每个架构更新
  • 我将“更新数据库”模块作为单独测试运行(由于已经应用了所有更改,因此不能执行任何操作)。或者,我再次拆掉DB并运行一次。
  • 我将测试数据加载到数据库中(如果修复数据错误,上面的一些测试会执行此操作)。

现在,测试数据库已准备好进行“真实”(应用程序)测试。在每次应用程序测试之后,我回滚当前事务,以便测试DB在安装后永远不会更改。

为了加快测试速度,我通常有三个测试套件:一个包含在数据库设置上,一个只包含应用程序测试,另一个包含其他两个套件。这允许我快速重置测试数据库并从应用程序套件中运行单个测试。

答案 3 :(得分:2)

我目前使用类似的方法。我一直在研究数据库迁移工具,但没有找到解决您描述的问题的工具。

问题在于,有时模式更改需要更改数据以允许创建新约束等...在这种情况下,如果忽略数据更新语句,则迁移将失败。

是否会在测试套件中添加一个sql脚本,删除数据库中的所有数据?

所以过程将是:

  1. 运行db migration。
  2. 运行脚本以删除db中的所有数据。
  3. 加载测试数据
  4. 运行测试

答案 4 :(得分:1)

我使用migrateDB来解决此问题。

此工具基于以下概念:您可以在数据库上执行“测试”(通过SQL),以查看是否已应用给定的数据库更改,以及在测试“失败时执行的相关操作集“。例如,您可能需要查询元表模式以查看表或列是否存在,如果不存在,则创建它。或者,您可能想要查看表中是否存在某个行,如果不存在,请插入它。它提供了一些预先配置的常见测试和操作,并且可以非常轻松地添加自己的(仅使用XML配置 - 无需执行此操作所需的新代码。)

作为一点奖励,这些测试和操作中的每一个都是为SQL的每个“方言”配置的(例如,你可以使用“oracle”方言和“mySQL”方言。)这意味着一旦你定义每个方言的给定测试和操作的查询,测试或操作的每个新实例不需要新的SQL,并且可以针对多个目标数据库执行。

然后,您只需维护一个列出测试和相应操作的小型XML文件,并在每次构建后对数据库运行该工具。

对我们来说效果很好。

答案 5 :(得分:0)

以下是我们的工作:

$ find src/sql/ | grep -v /.svn
src/sql/
src/sql/0000-system.sql
src/sql/0000-system.sql.dev.log
src/sql/0000-system.sql.prod.log
src/sql/0000-system.sql.test.log
src/sql/0001-usgn.sql
src/sql/0001-usgn.sql.dev.log
src/sql/0001-usgn.sql.prod.log
src/sql/0001-usgn.sql.test.log
src/sql/0002-usgn.sql
src/sql/0002-usgn.sql.dev.log
src/sql/0002-usgn.sql.prod.log
src/sql/0002-usgn.sql.test.log
src/sql/0003-usgn.sql
src/sql/0003-usgn.sql.dev.log
src/sql/0003-usgn.sql.prod.log
src/sql/0003-usgn.sql.test.log
src/sql/0004-system.sql
src/sql/0004-system.sql.dev.log
src/sql/0005-usgn.sql
src/sql/purge.sql

我们有scriptseq ### - databaseusercredential.sql

现在我们的测试总是允许数据库中数据的未知启动状态。如果你不能这样做,那么我建议你使用SEQ-CRED-TYPE.sql,其中type将是dml / ddl并过滤掉dml脚本。