前言:分享和规定命名规范后,各位测试人员一致认为这样jmeter的jmx文件限制太死,主要体现六方面:

第一:规定了一个jmx文件只能录入一个接口,这样会导致jmx文件很多

第二:导入DB的jmx文件每轮迭代都需要更换版本号,会带来额外的工作

第三:jmx文件严格要求了接口执行循序,会导致大家写好的用例会重新输出

第四:importDB的jmx文件与接口的jmx文件对应也会很多,不能一个importDB的jmx完成所有接口的工作

第五:删除了定义产品类型和页面类型字段,增加了接口类型字段

第六:测试反馈终端类型、版本号、接口类型不需要通过importDB 的jmx文件传入而是通过http请求名称按照一定规则去获取

因此针对上面四项不足,做了一些优化。

技术方面方面改变主要体现在:

第一:把终端类型、版本号、接口类型、接口名称、用例数目、用例成功数目、通过率这些字段,之前只有部分(接口类型、接口名称)是list结构,现在均改为list结构。
第二:java set操作的sql只有详细表,而统计表是通过sql操作的(通过select详细表计算出来统计表每个字段值insert和update的)
第三:联合唯一索引增加了一个字段creatTime,因为测试反应按照之前联合索引只能保留本轮迭代的数据,有之前的按照迭代保留数据,目前是按照当天保留数据(因此有之前精确到秒改为精确到日)

第四:SQL的各个字段获取规则进行了限制

jmeter线程请求命名规则:
由于代码做了字段获取的规则,循环到”终端、版本、类型、校验、接口”关键字时认为是本jmx文件需校验的接口和用例

终端类型获取规则:

由于代码做了终端类型获取的规则,截取”终端”和“版本”中间的字符作为终端类型

版本号获取规则:

由于代码做了版本号获取的规则,截取”终端”和“版本”中间的字符作为版本号

接口类型获取规则:
由于代码做了接口类型获取的规则,截取”终端”和“版本”中间的字符作为接口类型

接口名称获取规则:

由于代码做了接口名称获取的规则,截取”校验”和“接口”中间的字符作为接口名称

用例名称获取规则:

由于代码做了用例名称获取的规则,截取”接口”后面的字符作为接口名称

具体SQL key值的取值方法如下:

//获取符合规则的终端类型publicstaticArrayList<String>getTerminalType(Stringpath)throwsException{CsvUtilutil=newCsvUtil(path);introwNum=util.getRowNum();StringcaseAllName=null;StringTerminalType=null;ArrayList<String>terminalTypeArray=newArrayList<String>();for(inti=1;i<rowNum;i++){caseAllName=util.getString(i,2);if(caseAllName.contains("终端")&&caseAllName.contains("版本")==true&&caseAllName.contains("类型")&&caseAllName.contains("校验")==true&&caseAllName.contains("接口")==true){TerminalType=caseAllName.substring(caseAllName.indexOf("终端")+2,caseAllName.indexOf("版本"));terminalTypeArray.add(TerminalType);}}returnterminalTypeArray;}//获取符合规则的版本号publicstaticArrayList<String>getVersion(Stringpath)throwsException{CsvUtilutil=newCsvUtil(path);introwNum=util.getRowNum();StringcaseAllName=null;Stringversion=null;ArrayList<String>excVersionArray=newArrayList<String>();for(inti=1;i<rowNum;i++){caseAllName=util.getString(i,2);if(caseAllName.contains("终端")&&caseAllName.contains("版本")==true&&caseAllName.contains("类型")&&caseAllName.contains("校验")==true&&caseAllName.contains("接口")==true){version=caseAllName.substring(caseAllName.indexOf("版本")+2,caseAllName.indexOf("类型"));excVersionArray.add(version);}}returnexcVersionArray;}//获取符合规则的用例类型publicstaticArrayList<String>getInterfaceType(Stringpath)throwsException{CsvUtilutil=newCsvUtil(path);introwNum=util.getRowNum();StringcaseAllName=null;StringinterfaceNameType=null;ArrayList<String>interfaceTypeArray=newArrayList<String>();for(inti=1;i<rowNum;i++){caseAllName=util.getString(i,2);if(caseAllName.contains("终端")&&caseAllName.contains("版本")==true&&caseAllName.contains("类型")&&caseAllName.contains("校验")==true&&caseAllName.contains("接口")==true){interfaceNameType=caseAllName.substring(caseAllName.indexOf("类型")+2,caseAllName.indexOf("校验"));interfaceTypeArray.add(interfaceNameType);}}//System.out.println("获取接口名称:"+interfaceName);returninterfaceTypeArray;}//获取符合规则的接口名称publicstaticArrayList<String>getInterfaceName(Stringpath)throwsException{CsvUtilutil=newCsvUtil(path);introwNum=util.getRowNum();StringcaseAllName=null;StringinterfaceName=null;ArrayList<String>interfaceNameArray=newArrayList<String>();for(inti=1;i<rowNum;i++){caseAllName=util.getString(i,2);if(caseAllName.contains("终端")&&caseAllName.contains("版本")==true&&caseAllName.contains("类型")&&caseAllName.contains("校验")==true&&caseAllName.contains("接口")==true){interfaceName=caseAllName.substring(caseAllName.indexOf("校验")+2,caseAllName.indexOf("接口"));interfaceNameArray.add(interfaceName);//System.out.println("获取接口名称:"+interfaceName);}}//returninterfaceNameArray;}//获取符合规则的每条用例执行名称publicstaticArrayList<String>getCaseName(Stringpath)throwsException{CsvUtilutil=newCsvUtil(path);introwNum=util.getRowNum();StringcaseAllName=null;StringcaseName=null;ArrayList<String>caseNameArray=newArrayList<String>();for(inti=1;i<rowNum;i++){caseAllName=util.getString(i,2);if(caseAllName.contains("终端")&&caseAllName.contains("版本")==true&&caseAllName.contains("类型")&&caseAllName.contains("校验")==true&&caseAllName.contains("接口")==true){caseName=caseAllName.substring(caseAllName.indexOf("接口")+2,caseAllName.length());//System.out.println("用例名称为:"+caseName);caseNameArray.add(caseName);//System.out.println(caseNameArray);}}returncaseNameArray;}//获取符合规则的每条用例执行结果publicstaticArrayList<String>getcaseIsPass(Stringpath)throwsException{CsvUtilutil=newCsvUtil(path);introwNum=util.getRowNum();ArrayList<String>caseResultArray=newArrayList<String>();//System.out.println(CaseNum);StringcaseAllName=null;for(inti=1;i<rowNum;i++){caseAllName=util.getString(i,2);if(caseAllName.contains("终端")&&caseAllName.contains("版本")==true&&caseAllName.contains("类型")&&caseAllName.contains("校验")==true&&caseAllName.contains("接口")==true){Stringresult=util.getString(i,7);caseResultArray.add(result);}}returncaseResultArray;}

支持和兼容:
第一:支持同个jmx文件多个接口用例场景
第二:兼容同个jmx文件虽同一个接口不同顺序的输入

一个要求:
jmeter命名规则为终端AAA版本BBB类型CCC校验DDD接口EEE

AAA为终端类型(如app、网站) ,BBB为迭代号(如9.0.1) ,CCC为接口类型(如搜索、跟团) ,DDD为接口名称(如默认出发城市),EEE为用例名称(如检验推荐城市否正确)

由于获取SQL每个字段均为List,因此若jmeter想使用,必须进行二次封装!!!

//封装上面获取终端类型、版本号、接口类型、接口名称、方法提供jmeter使用publicstaticArrayList<String>getTerminalTypeArray(Stringpath)throwsException{ArrayList<String>terminalTypeArray=readCsv.getTerminalType(path);returnterminalTypeArray;}publicstaticArrayList<String>getVersionArray(Stringpath)throwsException{ArrayList<String>versionTypeArray=readCsv.getVersion(path);returnversionTypeArray;}publicstaticArrayList<String>getInterfaceTypeArray(Stringpath)throwsException{ArrayList<String>interfaceTypeArray=readCsv.getInterfaceType(path);returninterfaceTypeArray;}publicstaticArrayList<String>getInterfaceNameArray(Stringpath)throwsException{ArrayList<String>interfaceNameArray=readCsv.getInterfaceName(path);returninterfaceNameArray;}publicstaticArrayList<String>getCaseNameArray(Stringpath)throwsException{ArrayList<String>caseNameArray=readCsv.getCaseName(path);returncaseNameArray;}publicstaticArrayList<String>getCaseIsPassArray(Stringpath)throwsException{ArrayList<String>caseIsPassArray=readCsv.getcaseIsPass(path);returncaseIsPassArray;}

好了获取到了,我们做插入DB操作:

详细表跟之前改变不大!!(索引扩充一个createTime字段、时间有精确到秒改为精确到日)

如:

publicstaticStringcurrTime(){SimpleDateFormatdf=newSimpleDateFormat("yyyy-MM-dd");//设置日期格式Stringnow=df.format(newDate());//newDate()为获取当前系统时间returnnow;}//插入详细数据publicstaticbooleaninsertDetailDB(StringterminalType,StringexcVersion,StringinterfaceType,StringinterfaceName,StringcaseName,StringexcResult){try{Class.forName("com.mysql.jdbc.Driver");StringdatabaseName="test";//已经在MySQL数据库中创建好的数据库。StringuserName="mobtest";//MySQL默认的root账户名Stringpassword="tuniu520";//默认的root账户密码为空StringconnUrl="jdbc:mysql://10.10.30.200:3306/";//连接地址Connectionconn=DriverManager.getConnection(connUrl+databaseName,userName,password);PreparedStatementst=null;Statementstmt=conn.createStatement();StringcreateTime=readCsv.currTime();Stringsql="createtableifNOTEXISTSAutoTest_DetailInterface(idintNOTNULLauto_incrementprimarykey,terminalTypevarchar(50)NOTNULLDEFAULT'App',"+"excVersionvarchar(50),interfaceTypevarchar(50),interfaceNamevarchar(50),"+"caseNamevarchar(50),excResultvarchar(50),creatTimevarchar(50)NOTNULLDEFAULT'"+createTime+"',"+"UNIQUEINDEX(terminalType,excVersion,interfaceType,interfaceName,caseName,creatTime))";//System.out.println(sql);//创建数据库中的表,intresult=stmt.executeUpdate(sql);if(result!=-1){sql="insertintoAutoTest_DetailInterface(terminalType,excVersion,interfaceType,interfaceName,caseName,excResult)values(?,?,?,?,?,?)"+"ONDUPLICATEKEYUPDATEexcResult=?";//sql="insertintoAutoTest_DetailInterface(permaryTitle,secondaryTitle,excVersion,excTerminal,excResult)//+values(primaryTitle,secordaryTitle,excVersion,excTerminal,excResult)";st=conn.prepareStatement(sql);//存入终端类型转为小写st.setString(1,terminalType.toLowerCase());st.setString(2,excVersion);st.setString(3,interfaceType);st.setString(4,interfaceName);st.setString(5,caseName);//存入执行结果true或者false转为小写st.setString(6,excResult.toLowerCase());st.setString(7,excResult.toLowerCase());st.executeUpdate();sql="SELECT*FROMAutoTest_DetailInterface";System.out.println(stmt.executeQuery(sql));ResultSetrs=stmt.executeQuery(sql);System.out.println("id\tterminalType\texcVersion\tinterfaceType\tinterfaceName\tcaseName\texcResult\tcreatTime");while(rs.next()){System.out.println(rs.getString(1)+"\t"+rs.getString(2)+"\t"+rs.getString(3)+"\t"+rs.getString(4)+"\t"+rs.getString(5)+"\t"+rs.getString(6)+"\t"+rs.getString(7)+rs.getString(8));}}conn.close();}catch(Exceptione){e.printStackTrace();returnfalse;}returntrue;}

插入统计表:(相比之前思路都改变了~是通过详细表select计算统计后insert进去的而不是通过java依次获取每个字段值set进去的)

具体如下:

有一点说明:就是SQL在insert的时候若是insert整个sql的时候且当唯一联合索引键相同时,做更新不能用 key=vaule这种平常的用法(不然拿到第一行的数据更新了所有行的数据),而是 set key=VALUES(key)

具体用法可以看下http://www.jb51.net/article/39255.htm 这个用法,备注,因为这点纠结了我大半天时间!!!

//插入统计数据publicstaticbooleaninsertTotalDB(){try{Class.forName("com.mysql.jdbc.Driver");StringdatabaseName="test";//已经在MySQL数据库中创建好的数据库。StringuserName="mobtest";//MySQL默认的root账户名Stringpassword="tuniu520";//默认的root账户密码为空StringconnUrl="jdbc:mysql://10.10.30.200:3306/";//连接地址Connectionconn=DriverManager.getConnection(connUrl+databaseName,userName,password);PreparedStatementst=null;Statementstmt=conn.createStatement();StringcreateTime=readCsv.currTime();Stringsql="createtableifNOTEXISTSAutoTest_TotalInterface(idintNOTNULLauto_incrementprimarykey,terminalTypevarchar(50)NOTNULLDEFAULT'App',"+"excVersionvarchar(50),interfaceTypevarchar(50),interfaceNamevarchar(50),"+"caseTotalNumint,caseSucNumint,excRatevarchar(50),creatTimevarchar(50)NOTNULLDEFAULT'"+createTime+"',"+"UNIQUEINDEX(terminalType,excVersion,interfaceType,interfaceName,creatTime))";//创建数据库中的表,intresult=stmt.executeUpdate(sql);if(result!=-1){StringselectSQL="SELECTterminalType,excVersion,interfaceType,"+"interfaceName,caseTotalNum,caseSucNum,"+"ROUND(caseSucNum/caseTotalNum,3)"+"asexcRate,creatTimefrom("+"SELECTterminalType,excVersion,interfaceType,interfaceName,count(1)"+"ascaseTotalNum,sum(ex)ascaseSucNum,creatTimefrom("+"SELECTterminalType,excVersion,interfaceType,interfaceName,"+"creatTime,caseexcResult"+"when'true'then1when'false'then0endasexfrom"+"AutoTest_DetailInterface)mgroupby"+"terminalType,excVersion,interfaceType,interfaceName,creatTime)n";sql="insertintoAutoTest_TotalInterface("+"terminalType,excVersion,interfaceType,interfaceName,caseTotalNum,caseSucNum,"+"excRate,creatTime)"+selectSQL+"ONDUPLICATEKEYUPDATEcaseTotalNum=VALUES(caseTotalNum),caseSucNum=VALUES(caseSucNum),excRate=VALUES(excRate)";System.out.println(sql);//执行插入操作st=conn.prepareStatement(sql);st.executeUpdate();sql="SELECT*FROMAutoTest_TotalInterface";System.out.println(stmt.executeQuery(sql));ResultSetrs=stmt.executeQuery(sql);while(rs.next()){System.out.println(rs.getString(1)+"\t"+rs.getString(2)+"\t"+rs.getString(3)+"\t"+rs.getString(4)+"\t"+rs.getString(5)+"\t"+rs.getString(6)+"\t"+rs.getString(7)+"\t"+rs.getString(8)+rs.getString(9));}}conn.close();}catch(Exceptione){e.printStackTrace();returnfalse;}returntrue;}

好了,到此结束,当然,importDB的jmx文件也要发生变化(直接调用方法即可),具体如下:

importreadDB.*;importexcFile.*;Stringpath="D:\\excResult.csv";intlength=readCsv.getTerminalTypeArray(path).size();log.info("用例数量为:"+length);//循环获取各个参数for(inti=0;i<length;i++){StringterminalTypeKey=readCsv.getTerminalTypeArray(path).get(i);log.info("获取终端类型:"+terminalTypeKey);StringexcVersionKey=readCsv.getVersionArray(path).get(i);log.info("获取版本号:"+excVersionKey);StringinterfaceTypeKey=readCsv.getInterfaceTypeArray(path).get(i);log.info("获取接口类型:"+interfaceTypeKey);StringinterfaceNameKey=readCsv.getInterfaceNameArray(path).get(i);log.info("获取接口名称:"+interfaceNameKey);StringcaseNameKey=readCsv.getCaseNameArray(path).get(i);log.info("获取用例名称:"+caseNameKey);StringexcResultKey=readCsv.getCaseIsPassArray(path).get(i);log.info("获取执行结果:"+excResultKey);readCsv.insertDetailDB(terminalTypeKey,excVersionKey,interfaceTypeKey,interfaceNameKey,caseNameKey,excResultKey);}//插入统计表readCsv.insertTotalDB();Stringdir="D:\\";Stringoldname="excResult.csv";log.info("获取最新文件名称:"+renFile.currTime());Stringnewname="excResult"+renFile.currTime()+".csv";log.info("获取最新文件名称:"+newname);renFile.renameFile(dir,oldname,newname);

导入DB数据如下:

原始数据:

导入详细表的数据:

导入统计表数据: