DBUnit和Spring Boot - 在集成测试中请求时,可能无法导入或存在数据

时间:2014-10-07 12:29:34

标签: java spring spring-data spring-boot dbunit

如果我使用以下设置运行dbunit并在集成测试中通过HTTP请求数据,我没有得到任何数据,因为数据库是空的。 DBUnit将数据写入数据库,但是当我通过HTTP请求数据时它是空的。

这是我的设置: Spring Boot 1.1.7 with spring-boot-starter-web(不包括tomcat),spring-boot-starter-jetty,spring-boot-starter-data-jpa,spring-boot-starter-test,liquibase-core,dbunit 2.5 .0,spring-test-dbunit 1.1.0

主要申请类:

@Configuration
@ComponentScan
@EnableAutoConfiguration
@RestController
@EnableTransactionManagement
@EnableJpaRepositories

测试配置(application-test.yaml):

logging.level.org.springframework: DEBUG
logging.level.org.dbunit: DEBUG

spring.jpa.properties.hibernate.hbm2ddl.auto: update
spring.jpa.database: h2
spring.jpa.show-sql: true

// setting these properties to access the database via h2 console
spring.datasource.url: jdbc:h2:tcp://localhost/mem:my_db;DB_CLOSE_DELAY=-1;MVCC=true;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username: sa
spring.datasource.password: sa
spring.datasource.driverClassName: org.h2.Driver
spring.jpa.database-platform: org.hibernate.dialect.H2Dialect

liquibase.change-log: classpath:/db/changelog/db-master.xml

集成测试:

@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = HDImageService.class)
@TestExecutionListeners({
    DependencyInjectionTestExecutionListener.class,
    DbUnitTestExecutionListener.class })
@WebAppConfiguration
@IntegrationTest("server.port:0")
@DatabaseSetup("/database_seed.xml")
@DatabaseTearDown(value = "/database_tear_down.xml", type = DatabaseOperation.DELETE_ALL)

// test
@Test
public void get_works() throws Exception {
    // given
    String url = host + port + "/my-resource/1";

    // when
    ResponseEntity<String> response = template.getForEntity(url, String.class);

    // then
    assertThat(response.getStatusCode(), is(HttpStatus.OK));
}

我可以在这里发布其他所有内容,例如实体,存储库,控制器......但是这些组件正在运行,因为我已经通过注入的存储库写入测试数据库并通过HTTP获取它。所以问题是通过dbunit导入,这是行不通的...我已经在较旧的预测中成功使用了dbunit,但没有与spring boot一起使用。也许执行监听器的工作方式与spring boot不相似?

我调试了dbunit的类并读取了所有的调试日志输出,但是我没有得到它。 DBUnit正在使用spring boot创建的dataSource(上面配置的),所以它是相同的数据库。

开始集成测试时,会发生以下情况:
- liquibase基于liquibase配置创建数据库模式(也许jpa之前已经推送了模式?)
- DBUnit插入数据库(表示日志输出和调试)
- 找不到404(当在具有给定ID的数据库中找不到条目时,我返回404)

更新

我正在寻找dbunit的替代品,但找不到任何好的解决方案。那么如何为集成测试准备数据库呢?实际上,如果数据按预期持久存在,我只需要在每次测试或测试之前导入单个数据。

更新

我使用以下选项连接到h2数据库:

DB_CLOSE_DELAY=-1;MVCC=true;DB_CLOSE_ON_EXIT=FALSE

当我删除完整的spring.datasource。*配置弹簧正在创建具有标准值的数据源并启动内存中的h2数据库服务器。这将在没有我提到的选项的情况下完成,我得到org.hibernate.PessimisticLockException,因为dbunit仍然锁定数据库表,并且在测试中发送的HTTP请求无法访问数据库表。这是因为选项MVCC=true;增加了更高的并发性,基本上是问题,为什么没有数据存在:“仅连接”参见'已提交的数据和自己的更改'。通过HTTP请求访问数据库时,dbunit的数据不存在,因为没有为spring连接提交dbunit的数据...

那么,有谁知道为什么h2(以及derby)数据库表被dbunit锁定了?

1 个答案:

答案 0 :(得分:6)

我终于找到了解决这个问题的方法。

这是我指向PessimisticLockException的正确方向。 DBUnit没有释放数据库连接,这就是为什么spring连接无法访问锁定的数据库表的原因。

我实现了自己的数据库操作。我使用customize DBUnit database options选项。

首先,我基于DBUnit的TransactionOperation实现了一个名为AutoCommitTransactionOperation的类,区别在于我在设置自动提交之前删除了检查jdbcConnection.getAutoCommit() == false并保存了自动提交值为假。在提交之后,我将值设置回保存的值,以具有与之前相同的状态:

public class AutoCommitTransactionOperation extends DatabaseOperation {

    private final DatabaseOperation _operation;

    public AutoCommitTransactionOperation(DatabaseOperation operation) {
        _operation = operation;
    }

    public static final DatabaseOperation AUTO_COMMIT_TRANSACTION(DatabaseOperation operation) {
        return new AutoCommitTransactionOperation(operation);
    }

    public void execute(IDatabaseConnection connection, IDataSet dataSet) throws DatabaseUnitException, SQLException {
        logger.debug("execute(connection={}, dataSet={}) - start", connection, dataSet);

        IDatabaseConnection databaseConnection = connection;
        Connection jdbcConnection = databaseConnection.getConnection();

        boolean autoCommit = jdbcConnection.getAutoCommit();
        jdbcConnection.setAutoCommit(false);
        try {
            _operation.execute(databaseConnection, dataSet);
            jdbcConnection.commit();
        } catch (DatabaseUnitException e) {
            jdbcConnection.rollback();
            throw e;
        } catch (SQLException e) {
            jdbcConnection.rollback();
            throw e;
        } catch (RuntimeException e) {
            jdbcConnection.rollback();
            throw e;
        } finally {
            jdbcConnection.setAutoCommit(autoCommit);
        }
    }
}

然后我创建了DatabaseLookup。

public class AutoCommitTransactionDatabaseLookup extends DefaultDatabaseOperationLookup {

    @Override
    public org.dbunit.operation.DatabaseOperation get(DatabaseOperation operation) {
        if (operation == operation.CLEAN_INSERT) {
            return AutoCommitTransactionOperation.AUTO_COMMIT_TRANSACTION(org.dbunit.operation.DatabaseOperation.CLEAN_INSERT);
        }
        return super.get(operation);
    }
}

并将其添加到我的测试类中:

@DbUnitConfiguration(databaseOperationLookup = AutoCommitTransactionDatabaseLookup.class)

我不确定这是否更加黑客......对我的黑客有什么暗示?