其他测试框架 测试开发之路----一切为了效率 (数据恢复--AssertJ 的另类用法)

孙高飞 · May 31, 2016 · Last by Testjava replied at April 24, 2020 · 3115 hits

前言

本来今天的内容没打算放到这个系列里的,但其实这个帖子应该是这个系列的第一篇关于数据管理策略的后续。所以我想了想,还是放在这里面说吧。之前的帖子说过我们有共享数据和隔离数据,并讨论了针对这些数据在框架层面上的处理(如有不清楚的请看这个系列的第一篇帖子)。我们说明了注册式数据管理方式的缺点再于我们只能销毁我们注册进框架中的数据,如果被测功能本身就产生了脏数据的话,我们是没有办法的,只能写代码去删除了。但是最近终于让我找到了克服这个缺陷的方法,也就是框架终于可以监控到测试脚本运行前以及运行后的变化并将数据恢复到运行前的状态。我们再也没有脏数据的忧虑了。这是注册式数据管理的进阶版,我命名为监控式

原理

其实原理很通俗易懂,就是记录一下case运行前的状态,在case结束的时候再把数据恢复回去。以前有人建议我用docker快速起一个数据库实例来达到数据恢复的目的,我没有实验过效率怎么样。当时想的就是做一个通用的框架,即使没有docker也可以做到。所以只能另辟蹊径,之后发现我们java大名鼎鼎的断言神器AssertJ中有changes的概念,一下子我欣喜若狂,省去我自己去监控数据变化了。 AssertJ的changes概念已经做到了数据库的diff。

AssertJ

AssertJ 是目前java中最强大的断言开源项目了。它提供了各种各样强大的功能,具体的我在另一篇帖子中介绍。今天我们就看一看如何利用它的changes概念达到我们的目的。

BasicDataSource dataSource = SpringContext.getBean("dataSource", BasicDataSource.class);
Changes changes = new Changes(dataSource);

上面是创建一个changes的标准代码,它接受一个参数,java的DataSource。只不过我的项目用的是spring+mybatis构成的持久化层,所以代码就变成了上面那样。其实你也可以用下面的方式

Source source = new Source("jdbc:mysql://172.27.1.219:3306/dango?useUnicode=yes&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull", "write", "!@#$1234%^&*5678ABCDabcd");
Changes changes = new Changes(source);

这样你就已经为监控数据库中数据的变化做好了准备。如果你希望不监控整个数据库,只监控部分表的话。其实也可以这么写

Table dataTable = new Table(dataSource, "data");
Table migrationsTable = new Table(dataSource, "migrations");
Table model_appTable = new Table(dataSource, "model_app");
Changes change = new Changes(user_roleTable,user_metricTable,user_aclTable,userTable
,task_dataTable,role_aclTable,reportTable,project_dataTable,
planTable,projectTable,permissionTable,taskTable,roleTable,
model_app_historyTable,model_appTable,migrationsTable
,dataTable);

通过自定义各个table组建,我们也可以定制个别表的监控。那么接下来怎么做?我只需要在测试前开启监控,测试后关闭监控,然后抽取出数据变化,拼接sql语句,将数据恢复到之前的状态就好了。

methodDestory.setStartPoint();
methodDestory.setEndPoint();

上面第一行代码的意思就是开启监控,第二行就是结束监控。然后我们再调用getChangeList方法。

List<Change> changeList = this.changes.getChangesList();

这样我们就可以拿到所有的数据的变化了。之后我们要拼接出各种sql语句。

for(Change change:changeList){
ChangeType type = change.getChangeType();
String tableName = change.getDataName();
if("CREATION".equals(type.name())){
String id = change.getRowAtEndPoint().getValuesList().get(0).getColumnName();
Object value = change.getRowAtEndPoint().getValuesList().get(0).getValue();
String sql = "delete from "+tableName+" where "+id+" = "+value+"";
deletionSQLs.add(sql);
}else if("DELETION".equals(type.name())){
String sql = "insert into "+tableName+" values(";
List<Value> valuesList = change.getRowAtStartPoint().getValuesList();
for(Value value : valuesList){
Object columenValue = value.getValue();
sql = sql + "'" +columenValue+"',";
}
sql = sql.substring(0, sql.length()-1);
sql = sql +")";
insertSQLs.add(sql);
}else if("MODIFICATION".equals(type.name())){
String sql = "update "+tableName+" SET ";
List<Value> valuesList = change.getRowAtStartPoint().getValuesList();
for(Value value : valuesList){
Object columenValue = value.getValue();
String columnName = value.getColumnName();
sql = sql + columnName +"='"+columenValue+"' ,";
}
sql = sql.substring(0, sql.length()-1);
sql = sql + " where "+valuesList.get(0).getColumnName()+" = "+valuesList.get(0).getValue();
updateSQLs.add(sql);
}

}

可以看到,我们根据不同的change类型拼接出了不同的语句。只要我们在测试结束后执行这些sql就可以恢复数据了。

最终形态

@DataManage(baseData="defaultPlan_Project.xls,defaultTask_Plan_Project.xls",recoveryStrategy=RecoveryStrategy.CLASS)
public class TestCreateProject extends BaseCase {
@Test
public void test(){
this.registerData("defaultProject.xls",true);
System.out.println();
}

可以看到最终我们通过使用一个注解来规定我们的数据恢复策略。CLASS表示当前测试类中所有的用例结束后再恢复数据,METHOD表示每执行一个用例前都会初始化数据,用例结束后恢复数据。

结尾

感谢断言神器AssertJ,不仅丰富了我的断言库,还给我提供了如此强大的功能。越来越发现时刻关注各类开源项目的重要性。有些时候好的解决方案真的只在不经意间就发现了。这里真的不想再强调有些情况下测试数据销毁的重要性了,因为我已然强调了无数遍,我知道一定还会有人喷我的。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 17 条回复 时间 点赞

脏数据确实很烦人,有的时候报到开发那里,结果发现是脏数据的原因,太尴尬了。java这么强大的开源断言神器,我也找找python有没有

监控过程中,是只会监控到当前进程(或线程)对DB的操作,还是测试脚本之外的DB操作也会被记录呢?

#2楼 @liufei83 都会被监控到。

数据库内数据量比较大的话,会不会崩溃,内存溢出之类的,楼主最大用了多少数据?

#4楼 @carol_gao 因为暂时只是在自动化测试中用所以数据是比较小的。如果数据量大的话,我觉得会很慢的

#5楼 @ycwdaaaa 我觉得也会慢,不过这个思路真好,谢谢啦

#1楼 @jphtmt 话说,python有没有啊。。

#7楼 @1875884881 好像找到一个python should,但是还没用过

changes.setStartPointNow();开启监控后,一直停留在这里,不能执行下面的方法,会有什么情况导致的?

孙高飞 #10 · June 07, 2016 作者

#9楼 @cythina 可能是你数据库太大了吧。这个机制性能是瓶颈。数据库不能太大了

#10楼 @ycwdaaaa 就监控了3张表,没有监控整个数据库

孙高飞 [Topic was deleted] 中提及了此贴 28 Jun 18:51
孙高飞 论自动化测试脚本的质量与效率 中提及了此贴 11 Aug 16:42

楼主在另一文章回复中推荐了assertJ,转来这篇后深感assertJ的强大呀!自己试了下看到产生的记录真的被删除了。但还有个疑问哈,监控数据diff还包括了非测试脚本产生的数据,可以做到排除这些非测试脚本产生的数据吗?因为我是直接在公司测试环境上执行脚本,其他测试同事也会往测试环境数据库插入了数据。我感觉我是不是要维护一个只有我自己才会往里面插数据的环境😂

叶子 回复

一般来说是的~~ 你得维护一个你自己专用的数据库。 其实它就是记录数据库前后的状态。 把数据恢复回去罢了

adonisjph 回复

所以python 有没有呢😂 ,我好像没找到~~

adonisjph 回复

老哥,你找到没有

for循环的时候卡住了

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up