java - JUnit 测试在 H2 的 RUNSCRIPT 完成之前开始

标签 java database maven junit h2

我们有一个使用 Spring 和 MAVEN 的 Java 项目。在这个项目中,我们使用内存中的 H2 数据库对我们的 DAO/Repository 层执行多项测试。

在运行了一些测试之后,但并非总是如此,我们收到以下错误:

org.h2.jdbc.JdbcSQLException: Table "WEATHER" not found; SQL statement:

如果您单独执行 JUnit 测试,它永远不会失败。没有关于何时会出现错误的模式。

我怀疑下面的 URL 连接上的 RUNSCRIPT 语句没有完成,单元测试开始时,即执行是异步执行的。

连接语句如下:

String jdbcUrl = "jdbc:h2:mem:WeatherAPI;MODE=MySQL;DB_CLOSE_ON_EXIT=TRUE;TRACE_LEVEL_SYSTEM_OUT=1;INIT=runscript from 'src/test/resources/sql/weatherapi.sql'"

这个想法是在每次测试时重置数据库。

这是获取数据源对象的代码片段:

private static java.sql.DataSource ds = null;

public static DataSource getDs() {
    if(this.ds==null) {
        try {
            this.ds = manualCreateDataSource();
        } catch (Exception e) {
            logger.error("Could not initialize Datasource", e);
            throw new RuntimeException("Could not initialize Datasource");
        }
    }

    return this.ds;
}

public static DataSource manualCreateDataSource() {

    String driverClass = "org.h2.jdbcx.JdbcDataSource";
    String jdbcUrl = "jdbc:h2:mem:WeatherAPI;MODE=MySQL;DB_CLOSE_ON_EXIT=TRUE;TRACE_LEVEL_SYSTEM_OUT=1;INIT=runscript from 'src/test/resources/sql/weatherapi.sql'";
    int maxPoolSize = 20;
    int minPoolSize = 5;
    int unreturnedConnectionTimeout = 10;
    int idleConnectionTestPeriod = 200;
    int maxIdleTime = 1000;
    int maxStatementsPerConnection = 5;

    ComboPooledDataSource ds = new ComboPooledDataSource();
    ds.setJdbcUrl(jdbcUrl);
    ds.setMaxPoolSize(maxPoolSize);
    ds.setMinPoolSize(minPoolSize);
    ds.setInitialPoolSize(minPoolSize);
    ds.setUnreturnedConnectionTimeout(unreturnedConnectionTimeout);
    ds.setIdleConnectionTestPeriod(idleConnectionTestPeriod);
    ds.setMaxIdleTime(maxIdleTime);
    ds.setMaxStatementsPerConnection(maxStatementsPerConnection);

    try {
        ds.setDriverClass(driverClass);
    } catch (PropertyVetoException e) {
        logger.error("error setting driver class", e);
    }

    return ds;
}

这里是 weatherapi.sql 脚本的片段:

CREATE SCHEMA IF NOT EXISTS `WeatherAPI`;
USE `WeatherAPI`;

DROP TABLE IF EXISTS `Weather`;
CREATE TABLE IF NOT EXISTS `Weather` (
    id int(11) NOT NULL AUTO_INCREMENT,
    location char(3) NOT NULL,
    period varchar(8) NOT NULL,
    duration char DEFAULT NULL,
    payload TEXT,
    created timestamp NULL DEFAULT NULL,
    lastmodified timestamp NULL DEFAULT NULL,
    version int(11) NOT NULL,  PRIMARY KEY (id)
);

最佳答案

我怀疑这是一个竞争条件。根据documentation该脚本针对连接到数据库的每个客户端执行。由于您总是丢弃 Weather在重新创建表格之前,可能会在测试 A 时发生已经在运行,第二个客户端 B连接到数据库,表就在 A 的眼皮底下掉落了. B可能是另一个并行运行的测试或同一测试中的第二个线程。

如果是这种情况,您可以尝试使用 RunScript您的工具 manualCreateDataSource()方法而不是 INIT JDBC 连接 URL 中的参数:

String jdbcUrl = "jdbc:h2:mem:WeatherAPI;MODE=MySQL;DB_CLOSE_ON_EXIT=TRUE;TRACE_LEVEL_SYSTEM_OUT=1;"
RunScript.execute(jdbcUrl, sa, "", "src/test/resources/sql/weatherapi.sql", null, false);

另外,你需要制作getDs()线程安全,通过添加 synchronized或者更好的是,通过初始化实例变量 ds静态的:

private static java.sql.DataSource ds = manualCreateDataSource();

public static DataSource getDs() {
    return ds;
}

关于java - JUnit 测试在 H2 的 RUNSCRIPT 完成之前开始,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31547146/

相关文章:

java - 从异步处理程序返回值到包含方法

mysql - 删除并重新创建 FK 约束或禁用并启用 FK 检查哪个更好?

SQL Server 丢失备份

java - 无法解析 no.nordicsemi.android.support.v18 :scanner

java - `null` 被当作字符串?

java - 避免隐式急切加载集合 stub ?

javascript - TOMCAT报告此错误: INFO: Character decoding failed

php - 如何根据不同的条件做两个MySQL SUM

Maven ant 任务部署到公共(public)关系存储库而不是指定的 url

xml - Saxon 找不到函数 : current-group