构建期间jUnit中出现多个嵌入式HSQLDB数据库错误

时间:2015-01-26 20:05:22

标签: spring spring-batch junit4 hsqldb spring-test

我正在开发一个新的Spring Batch(3.0.3.RELEASE)应用程序,在该应用程序中将访问多个数据库。为了测试,我们使用HSQLDB(2.3.2)作为嵌入式数据库。

在我的应用程序上下文中,我有以下内容。

<jdbc:embedded-database id="dataSource">
</jdbc:embedded-database>

<jdbc:embedded-database id="proDataSource">
    <jdbc:script location="classpath:script-tables.sql" />
    <jdbc:script location="classpath:script-constraints.sql" />
</jdbc:embedded-database>

<jdbc:embedded-database id="altDataSource">
    <jdbc:script location="classpath:script-alt-tables.sql" />
</jdbc:embedded-database>

当我在Eclipse中运行单个测试时,事情很好。当我从命令行构建时,在第一次测试之后,我得到错误

Failed to execute SQL script statement at line 3 of resource class path resource [script-promrkt-promo.sql]
object name already exists: PROMRKT

在我看来,EmbeddedDatabaseFactory中的填充过程正在接收已填充的数据库。据我所知,每次测试后都没有执行SHUTDOWN,HSQLDB将已经填充的数据库留在内存中。

我已经重新审核了文档,并在Spring Doc中显示了一个明确的关闭命令。但是,如果Spring在我的测试开始时启动嵌入式数据库,为什么在测试完成时它没有关闭它?

  1. 在相同应用程序上下文的每个单元测试之后,是否预期嵌入式数据库会保留?
  2. spring启动嵌入式数据库的顺序是什么,以及何时初始化事务上下文?
  3. 我是否需要使用数据库清理程序?
  4. 可以将填充更新为仅在首次启动数据库时填充,并在我的测试完成时回滚到原始脚本配置(有点像AbstractTransactionalSpringContextTests的工作方式)
  5. 我需要一些交易标记吗? Spring Batch的JobRepo在每次测试之间正确填充和销毁。为什么我的自定义数据源不是?

3 个答案:

答案 0 :(得分:3)

日志消息抱怨的脚本不在您的配置中。我认为它在其他地方被执行了吗?如果是这种情况,您可能需要在测试中添加@DirtiesContext,以便Spring不会缓存上下文(我假设您正在使用SpringJunit4Runner@ContextConfiguration @DirtiesContext,但无法确定,因为您的实际测试不在问题中)。

如果我的假设是正确的,Spring会缓存上下文,以提高运行单元测试套件的性能。如果您的测试以可能影响其他测试的方式修改上下文(例如在一个测试中运行脚本需要在其他测试中再次运行),则使用{{1}}标记测试,而Spring不会缓存上下文。您可以在方法或类级别使用注释。您可以在此处详细了解注释:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html

答案 1 :(得分:1)

我花了很多时间看这个并阅读Spring Framework文档(喘气!)并通过代码进行跟踪。 4.1 spring核心有一些有趣的变化,特别是测试。

我发现ApplicationContext现在缓存在JVM级别。如果第二个测试要求上下文,TestContext首先在其缓存中查看是否有其他测试已经要求相同的配置。

我的一些测试配置文件。具有不同配置文件但具有相同@ContextConfiguration的测试会导致使用应用的配置文件重新加载该上下文。当&#34; Bean Loader&#34;在创建嵌入式数据库时,EmbeddedDatabaseFactory没有考虑嵌入式数据库(在内存HSQLDB中)可能已经从以前的测试中创建或缓存,并且不需要重新初始化。

因此,我在EmbeddedDatabaseFactory.initDatabase()中添加了一些逻辑,检查数据库是否已经存在,然后重新初始化&amp;运行DatabasePopulator。

    List existingDataBases = org.hsqldb.DatabaseManager.getDatabaseURIs();

    boolean isExisting = false;
    String localDBName = StringUtils.lowerCase(this.databaseName);

    for (Object object : existingDataBases) {
        if (object.toString().contains(localDBName)) {
            isExisting = true;
            break;
        }
    }

    // Now populate the database
    if (!isExisting && this.databasePopulator != null) {

(当然,对于弹簧需要的东西来说,这并不是很合理,但它可以解决问题)

在我看来,它看起来像是EmbeddedDatabaseFactory和TestContext缓存机制的部分问题。我的&#34; jdbc:嵌入式数据库&#34;定义没有与之关联的任何配置文件。为什么缓存需要重新创建它们而不是从现有的缓存bean中加载它们?

答案 2 :(得分:0)

每次创建新对象时,您都可以尝试通过generateUniqueName(true)设置唯一名称来强制创建新的嵌入式数据库。

以下是一个例子:

embeddedDatabase = new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .generateUniqueName(true)
            .addScripts("db/sql/create-db.sql", "db/sql/insert-data.sql")
            .build();