数据库断言可能会涉及以下的一些场景
1)判断某个数据库表内容相等
2)判断多个数据库表内容相等
可能需要考虑的场景
3)数据集中各记录的顺序
4)数据中各个列的顺序
5)数据的某些列,如时间戳、序列号
6)通过正则表达式来验证某些列,而不是忽略
7)通过replace来替换某些列的数据再进行比较
8)包含关系,而不是相等关系
我们将使用DataBaseRider提供的 @ExpectedDataSet注解来实现上述需求。
逐一来看一下。
@Test
@DataSet(cleanBefore = true)//<1>
@ExpectedDataSet(value = "yml/expectedUsers.yml", ignoreCols = "id")
public void shouldMatchExpectedDataSet() {
EntityManagerProvider instance = EntityManagerProvider.newInstance("rules-it");
User u = new User();
u.setName("expected user1");
User u2 = new User();
u2.setName("expected user2");
instance.tx().begin();
instance.em().persist(u);
instance.em().persist(u2);
instance.tx().commit();
}
DBRider支持xml、csv等传统的数据文件格式,也新增了对json、yaml文件格式的支持。
某些情况下,需要对多个数据表的结果进行验证。
@Test@DataSet(cleanBefore = true, transactional = true)
@ExpectedDataSet(value = {"yml/user.yml", "yml/tweet.yml"}, ignoreCols = {"id", "user_id"})
public void shouldMatchMultipleDataSets() {
User u = new User();
u.setName("@realpestano");
User u2 = new User();
u2.setName("@dbunit");
em().persist(u);
em().persist(u2);
Tweet t = new Tweet();
t.setContent("dbunit rules again!");
em().persist(t);}
某些数据集,如果保存在List之类的数据结构中,可以保持记录的顺序,所以在将数据集写入数据库时,可能每次执行程序时得到的记录顺序是一致的。但是某些其他类型的数据结构,如map,可能每次写库后的记录之间的顺序是在变化的。如果直接使用@ExpectedDataSet注解进行断言时,会抛出“数据顺序不一致”的结果,导致断言失败。
如果数据顺序不是断言时的关注点,可以借助DBRider在提供的@ExpectedDataSet注解中的orderBy属性来解决上述问题。orderBy会将预期结果的DataSet和数据库表中的实际结果首先根据orderBy提供的列进行排序,然后将排序过的结果进行断言。只要排序后的结果保持一致,那么断言结果就是可信的了。如以下的案例
@Test
@DataSet(value = "yml/empty.yml", disableConstraints = true)
@ExpectedDataSet(value = "yml/expectedUsersIgnoreOrder.yml", orderBy = "name")
public void shouldMatchExpectedDataSetIgnoringRowOrder() {
User u1 = new User();
u1.setName("@arhohuttunen");
User u2 = new User();
u2.setName("@realpestano");
User u3 = new User();
u3.setName("@dbunit");
tx().begin();
em().persist(u1);
em().persist(u2);
em().persist(u3);
tx().commit();}
可能有读者会问,如果预期结果和实际结果表的列的顺序不一致,断言结果是什么呢?目前来看,调整表的列的顺序属于schema变更,断言结果会是失败。
数据表的某些列中的数据,在自动化用例每次执行时,可能其结果是会变化的。如以下的两个场景
在很多金融系统的应用中,要求记录操作的人员和时间来作为后续的审核用。由于用例执行时间的不同,每次得的结果会不一样。
另外,在进行创建新的申请、下一个订单等类型的操作时,通常都会给记录一个序列号。简单的,可以通过数据库Sequence的方式来获取,或者调用专门的序列号生成服务来获取。由于自动化用例在执行时,可能没有去重置Sequence或者用例执行的顺序不一致,导致获取到的序列号也会各不相同。
在这些情况下,为了简化断言,可以将数据中的上述类型的列进行简单的忽略,排除这些列以后再行比较。
@Test@DataSet(value = "yml/user.yml", disableConstraints = true)@ExpectedDataSet(value = "yml/expectedUser.yml", ignoreCols = "id")public void shouldMatchExpectedDataSetAfterSeedingDataBase() { tx().begin(); em().remove(EntityManagerProvider.em().find(User.class, 1L)); tx().commit();}
在某些测试场景中,可能要求测试用例在断言时不能简单地对某些列进行忽略,虽然不能检查数据的具体值,但是希望能检查数据是否符合某些业务规则,譬如时间戳格式或者是序列号格式。这些场景,可以通过所谓的正则匹配断言来实现。对于DBRider来说,只要将预期结果的数据集中的数据的某些列修改成正则表达式即可。
USER:
- ID: "regex:\\d+"
NAME: regex:^expected user.* #expected user1
- ID: "regex:\\d+"
NAME: regex:.*user2$ #expected user2
这部分的需求原先来自对于null值的处理。在导出数据库数据时,各个数据库和文件类型对于Null值数据的处理是各不相同的,有些是直接忽略,有些则是只有Key,没有value,或者是 key=[null]。对于第一种来说,由于前面提到的在比较数据集时,如果没有忽略这些列的话,断言就会失败,因为两边数据集的列个数不一样。由于Nullable的列非常常见,如果只采用忽略策略则数据集断言的应用场景就非常受限了。
因此,DBRider在@DataSet和@ExpectedDataSet时都提供了replacer的概念。就是在导入或者比较时,将文件数据集的某些列的内容替换成为某种占位符。如在导出文件时将null值替换为[null]写入文件内容,然后在断言时再通过replacer算法进行替换和断言,从而解决上述断言失败的问题。
DBRider还提供了DateTimeReplacer来解决一部分日期时间替换的问题,这样前述提到的操作日期等数据列也可以实现断言了。用户也可以参考CustomRelacer来实现repalcer接口,实现自定义的替换方法,解决数据导出和断言时的数据列替换问题。
在新增记录类型的测试用例中,通常需要比较的是数据库中新增记录的内容是否与预期结果的相一致。如新建一个用户A,则会比较该用户是否在User表中,至于User表中的其它内容,则不需要比较或者无法比较(如其它用例中也新建了用户,影响了User表的内容且未回退)。这种情况下,可以使用@ExpectedDataSet中的contains比较方法。
@Test
@DataSet(value = "yml/user.yml", transactional = true)
@ExpectedDataSet(value = "yml/expectedUsersContains.yml", compareOperation = CompareOperation.CONTAINS)
public void shouldMatchExpectedDataSetContains() {
User u = new User();
u.setId(3);
u.setName("@dbrider");
em().persist(u);
}
以上用例均来自DBRider项目自身的测试用例。