我使用的是第一种方法,但有一点不同,可以解决您提到的问题。
运行DAO测试所需的一切都在源代码控制中。它包括用于创建数据库的架构和脚本(泊坞窗对此非常有用)。如果嵌入式DB可以使用-我可以提高速度。
与其他描述的方法的重要区别在于,测试所需的数据不是从SQL脚本或XML文件加载的。一切(除了某些有效地不变的字典数据)都是由应用程序使用实用程序函数/类创建的。
主要目的是使数据被测试使用
- 非常接近测试
- 明确的(使用SQL文件存储数据使查看哪些数据使用什么测试变得非常困难)
- 将测试与无关的更改隔离开来。
从根本上讲,这意味着这些实用程序仅允许在测试本身中声明性地指定对测试必不可少的内容,而忽略不相关的内容。
为了弄清楚它在实践中的含义,请考虑对使用编写的Comment
s到Post
s的DAO进行测试Authors
。为了测试此类DAO的CRUD操作,应在数据库中创建一些数据。测试看起来像:
@Test
public void savedCommentCanBeRead() {
// Builder is needed to declaratively specify the entity with all attributes relevant
// for this specific test
// Missing attributes are generated with reasonable values
// factory's responsibility is to create entity (and all entities required by it
// in our example Author) in the DB
Post post = factory.create(PostBuilder.post());
Comment comment = CommentBuilder.comment().forPost(post).build();
sut.save(comment);
Comment savedComment = sut.get(comment.getId());
// this checks fields that are directly stored
assertThat(saveComment, fieldwiseEqualTo(comment));
// if there are some fields that are generated during save check them separately
assertThat(saveComment.getGeneratedField(), equalTo(expectedValue));
}
与带有测试数据的SQL脚本或XML文件相比,这具有多个优点:
- 维护代码要容易得多(例如,在许多测试中引用的某些实体(例如Author)中添加必需列,不需要更改大量文件/记录,而只需更改生成器和/或工厂)。
- 特定测试所需的数据在测试本身中描述,而不在其他文件中描述。这种接近性对于测试可理解性非常重要。
回滚与提交
我发现测试在执行时提交会更方便。首先,一些影响(例如DEFERRED CONSTRAINTS
如果提交从未发生,则无法检查)。其次,当测试失败时,可以在数据库中检查数据,因为该数据不会被回滚还原。
原因是这样做的不利之处在于测试可能会产生损坏的数据,这将导致其他测试失败。为了解决这个问题,我尝试隔离测试。在上面的示例中,每个测试都可能创建新的测试,Author
并且所有其他与之相关的实体也会创建,因此冲突很少发生。为了处理可能被破坏但不能表示为数据库级别约束的其余不变量,我对一些可能在每次测试后运行的错误条件进行了程序检查(它们在CI中运行,但通常会在本地关闭以提高性能)原因)。