Spring + DbUnit 测试

羿昊英
2023-12-01

Spring dbunit

spring-test-dbunit 提供了 Spring 与 DbUnit 的集成,在 Spring 中我们可以使用注解来进行数据的准备和清理。

配置

为了使用 DbUnit 的注解,我们需要使用 DbUnitTestExecutionListener 类,下面是典型的使用 JUnit 4 的注解

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
       DirtiesContextTestExecutionListener.class,
      TransactionalTestExecutionListener.class,
       DbUnitTestExecutionListener.class })

准备与清理

完成配置后,我们需要使用的注解是 @DatabaseSetup@DatabaseTearDown 来进行数据库的数据初始化和重置。

初始化

下面是典型的注解使用,这里我们使用 sampleData.xml 作为数据文件,这个文件通常在测试类的同一个包下。

@DatabaseSetup("sampleData.xml")

你也可以使用其它路径,如

@DatabaseSetup("/META-INF/dbtest/sampleData.xml")

默认情况下,初始化的时候会进行 CLEAN_INSERT 操作,这意味着所有在数据文件,即这里的 sampleData.xml 中的表的数据,在插入数据时会被删除。这个是 DBUnit 的其中一个行为,详细的参考 DBUnit 的文档。

重置

@DatabaseTearDown 注解用来在测试完成之后重置数据,与 @DatabaseSetup 类似,可以使用在类上或方法上,它们有一样的属性可以设置。

注意:如果你将此注解与 @Transactional 一起使用,你可能需要更改配置。下面会有相应解释。

验证

@ExpectedDatabase 用来验证数据库的数据是否和期待的一样。比如当我们插入或删除数据的时候,就很有用。这个注解可以使用在类或方法上,当在类上使用时,每一个测试方法都会进行数据比较。

用法:

@ExpectedDatabase("expectedData.xml")

@ExpectedDatabase 支持两种不同的模式,DatabaseAssertionMode.DEFAULT 是默认的模式,它比较整个数据库。DatabaseAssertionMode.NON_STRICT 会忽略没有在期待的数据集文件中出现的表或列。通常后一种模式是常用的,因为我们的方法通常情况下只会影响几个表或几个列。

事务

如果你一同使用了 DbUnitTestExecutionListenerTransactionalTestExecutionListener 你可能会遇到与事务相关的问题。比如事务没有开始或在数据验证之前进行了回滚等。为了在 DBUnit 中使用事务,我们应该使用 TransactionDbUnitTestExecutionListener

修改后的配置如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
       DirtiesContextTestExecutionListener.class,
       TransactionDbUnitTestExecutionListener.class })

事务会在 @DatabaseSetup 之前启动,在 @DatabaseTearDown@ExpectedDatabase 之后结束。

示例

我们有两个服务类:
StudentService 学生服务类
ClazzService 班级服务类

ClazzService 有一个删除方法,删除班级时,先删除该班级下的所有学生

@Transactional
public void delete(Long id) {
    studentService.deleteByClazzId(id);
    clazzDao.delete(id);
}

下面的例子针对这个方法进行测试。

数据准备

  • 初始数据 setup.xml ,这里是 dbunit 的 FlatXmlDataSet 格式,元素名代表表名,属性对应字段名。
<?xml version="1.0" encoding="utf-8" ?>
<dataset>
    <student id="1" name="student1" clazz_id="1"/>
    <student id="2" name="student2" clazz_id="2"/>

    <clazz id="1" name="clazz1"/>
    <clazz id="2" name="clazz2"/>
</dataset>

假设我们删除了班级 id 为 1 的班级,则我们期待的数据库数据为 delete-result.xml

<?xml version="1.0" encoding="utf-8" ?>
<dataset>
    <student id="2" name="student2" clazz_id="2"/>

    <clazz id="2" name="clazz2"/>
</dataset>

编写测试

ClazzServiceTest

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application-context.xml")
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        TransactionDbUnitTestExecutionListener.class})
public class ClazzServiceTest {
    @Autowired ClazzService clazzService;

    @Test
    @Transactional
    @DatabaseSetup("setup.xml")
    @ExpectedDatabase("delete-result.xml")
    public void testDelete() {
        clazzService.delete(1L);
    }
}

有时候我们对多个方法进行测试的时候,对数据库进行了多次删除、插入操作的时候,@Transactional 能使数据库数据回滚,但是自增字段不会重设,这时候对我们的测试就会造成干扰,因为我们无法确定下一次插入数据时自增字段的值。关于这个问题参考 这篇博文

 类似资料: