我使用的是第一种方法,但有点不同,可以解决你提到的问题。
为 DAO 运行测试所需的一切都在源代码控制中。它包括用于创建数据库的模式和脚本(docker 对此非常有用)。如果可以使用嵌入式数据库 - 我使用它来提高速度。
与其他描述的方法的重要区别在于,测试所需的数据不是从 SQL 脚本或 XML 文件加载的。一切(除了一些有效不变的字典数据)都是由应用程序使用实用程序函数/类创建的。
主要目的是让测试使用的数据
- 非常接近考试
- 显式(使用 SQL 文件获取数据使得查看哪些数据被哪些测试使用非常有问题)
- 将测试与不相关的更改隔离开来。
这基本上意味着这些实用程序允许在测试本身中以声明方式仅指定测试所必需的内容,并省略不相关的内容。
为了了解它在实践中的含义,请考虑对某些 DAO 的测试,该测试与Comment
s to Post
s 编写的s 一起使用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 文件相比,这有几个优点:
- 维护代码要容易得多(例如在许多测试中引用的某些实体中添加强制列,例如作者,不需要更改大量文件/记录,而只需更改构建器和/或工厂)
- 特定测试所需的数据在测试本身中描述,而不是在其他文件中。这种接近性对于测试可理解性非常重要。
回滚与提交
我发现测试在执行时提交更方便。DEFERRED CONSTRAINTS
首先,如果提交从未发生,则无法检查某些效果(例如)。其次,当测试失败时,可以在数据库中检查数据,因为它不会被回滚还原。
当然,这有一个缺点,即测试可能会产生损坏的数据,这将导致其他测试失败。为了解决这个问题,我尝试隔离测试。在上面的示例中,每个测试都可能创建新的Author
,并且所有其他实体都与其相关,因此很少发生冲突。为了处理可能被破坏但不能表示为数据库级别约束的剩余不变量,我使用一些编程检查可能在每次测试后运行的错误条件(它们在 CI 中运行,但通常在本地关闭以提高性能原因)。