JMeter启动时会自动判断操作系统的locale 并选择合适的语言启动,启动jmeter后会出现一个中文界面。远程运行jmeter,测试人员可以跨越多台低端计算机复制测试,这样就可以模拟一个比较大的服务器压力,一个jmeter客户端实例,理论上可以控制任意多的远程jmeter实例,并通过他们收集测试数据。
分享和规定命名规范后,各位测试人员一致认为这样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值的取值方法如下:
//获取符合规则的终端类型 public static ArrayList<String> getTerminalType(String path) throws Exception{ CsvUtil util = new CsvUtil(path); int rowNum = util.getRowNum(); String caseAllName = null; String TerminalType = null; ArrayList<String> terminalTypeArray = new ArrayList<String>(); for(int i=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); } } return terminalTypeArray; } //获取符合规则的版本号 public static ArrayList<String> getVersion(String path) throws Exception{ CsvUtil util = new CsvUtil(path); int rowNum = util.getRowNum(); String caseAllName = null; String version = null; ArrayList<String> excVersionArray = new ArrayList<String>(); for(int i=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); } } return excVersionArray; } //获取符合规则的用例类型 public static ArrayList<String> getInterfaceType(String path) throws Exception{ CsvUtil util = new CsvUtil(path); int rowNum = util.getRowNum(); String caseAllName = null; String interfaceNameType = null; ArrayList<String> interfaceTypeArray = new ArrayList<String>(); for(int i=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); return interfaceTypeArray; } //获取符合规则的接口名称 public static ArrayList<String> getInterfaceName(String path) throws Exception{ CsvUtil util = new CsvUtil(path); int rowNum = util.getRowNum(); String caseAllName = null; String interfaceName = null; ArrayList<String> interfaceNameArray = new ArrayList<String>(); for(int i=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); } } // return interfaceNameArray; } //获取符合规则的每条用例执行名称 public static ArrayList<String> getCaseName(String path) throws Exception{ CsvUtil util = new CsvUtil(path); int rowNum = util.getRowNum(); String caseAllName = null; String caseName = null; ArrayList<String> caseNameArray = new ArrayList<String>(); for(int i=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); } } return caseNameArray; } //获取符合规则的每条用例执行结果 public static ArrayList<String> getcaseIsPass(String path) throws Exception{ CsvUtil util = new CsvUtil(path); int rowNum = util.getRowNum(); ArrayList<String> caseResultArray = new ArrayList<String>(); // System.out.println(CaseNum); String caseAllName = null; for(int i=1;i<rowNum;i++){ caseAllName = util.getString(i, 2); if(caseAllName.contains("终端")&&caseAllName.contains("版本")==true&&caseAllName.contains("类型")&&caseAllName.contains("校验")==true&&caseAllName.contains("接口")==true){ String result = util.getString(i, 7); caseResultArray.add(result); } } return caseResultArray; } |
支持和兼容:
第一:支持同个jmx文件多个接口用例场景
第二:兼容同个jmx文件虽同一个接口不同顺序的输入
一个要求:
jmeter命名规则为终端AAA版本BBB类型CCC校验DDD接口EEE
AAA为终端类型(如app、网站) ,BBB为迭代号(如9.0.1) ,CCC为接口类型(如搜索、跟团) ,DDD为接口名称(如默认出发城市),EEE为用例名称(如检验推荐城市否正确)
由于获取SQL每个字段均为List,因此若jmeter想使用,必须进行二次封装。
//封装上面获取终端类型、版本号、接口类型、接口名称、方法提供jmeter使用 public static ArrayList<String> getTerminalTypeArray(String path) throws Exception{ ArrayList<String> terminalTypeArray = readCsv.getTerminalType(path); return terminalTypeArray; } public static ArrayList<String> getVersionArray(String path) throws Exception{ ArrayList<String> versionTypeArray = readCsv.getVersion(path); return versionTypeArray; } public static ArrayList<String> getInterfaceTypeArray(String path) throws Exception{ ArrayList<String> interfaceTypeArray = readCsv.getInterfaceType(path); return interfaceTypeArray; } public static ArrayList<String> getInterfaceNameArray(String path) throws Exception{ ArrayList<String> interfaceNameArray = readCsv.getInterfaceName(path); return interfaceNameArray; } public static ArrayList<String> getCaseNameArray(String path) throws Exception{ ArrayList<String> caseNameArray = readCsv.getCaseName(path); return caseNameArray; } public static ArrayList<String> getCaseIsPassArray(String path) throws Exception{ ArrayList<String> caseIsPassArray = readCsv.getcaseIsPass(path); return caseIsPassArray; } |
好了获取到了,我们做插入DB操作:
详细表跟之前改变不大!!(索引扩充一个createTime字段、时间有精确到秒改为精确到日)
如:
public static String currTime(){ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");//设置日期格式 String now = df.format(new Date());// new Date()为获取当前系统时间 return now; } //插入详细数据 public static boolean insertDetailDB(String terminalType,String excVersion,String interfaceType,String interfaceName,String caseName,String excResult){ try { Class.forName("com.mysql.jdbc.Driver"); String databaseName = "test";// 已经在MySQL数据库中创建好的数据库。 String userName = "mobtest";// MySQL默认的root账户名 String password = "tuniu520";// 默认的root账户密码为空 String connUrl = "jdbc:mysql://10.10.30.200:3306/";//连接地址 Connection conn = DriverManager.getConnection(connUrl + databaseName, userName, password); PreparedStatement st = null; Statement stmt = conn.createStatement(); String createTime = readCsv.currTime(); String sql = "create table if NOT EXISTS AutoTest_DetailInterface(id int NOT NULL auto_increment primary key ,terminalType varchar(50) NOT NULL DEFAULT 'App' ," + "excVersion varchar(50),interfaceType varchar(50) ,interfaceName varchar(50)," + "caseName varchar(50) ,excResult varchar(50),creatTime varchar(50) NOT NULL DEFAULT '"+createTime+"'," + " UNIQUE INDEX ( terminalType,excVersion,interfaceType,interfaceName,caseName,creatTime ) )"; // System.out.println(sql); // 创建数据库中的表, int result = stmt.executeUpdate(sql); if (result != -1) { sql = "insert into AutoTest_DetailInterface(terminalType,excVersion,interfaceType,interfaceName,caseName,excResult) values(?,?,?,?,?,?) " + "ON DUPLICATE KEY UPDATE excResult=?"; // sql = "insert into AutoTest_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 * FROM AutoTest_DetailInterface"; System.out.println(stmt.executeQuery(sql)); ResultSet rs = 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 (Exception e) { e.printStackTrace(); return false; } return true; } |
插入统计表:(相比之前思路都改变了~是通过详细表select计算统计后insert进去的而不是通过java依次获取每个字段值set进去的)
具体如下:
有一点说明:就是SQL在insert的时候若是insert整个sql的时候且当唯一联合索引键相同时,做更新不能用 key=vaule这种平常的用法(不然拿到第一行的数据更新了所有行的数据),而是 set key=VALUES(key)
//插入统计数据 public static boolean insertTotalDB(){ try { Class.forName("com.mysql.jdbc.Driver"); String databaseName = "test";// 已经在MySQL数据库中创建好的数据库。 String userName = "mobtest";// MySQL默认的root账户名 String password = "tuniu520";// 默认的root账户密码为空 String connUrl = "jdbc:mysql://10.10.30.200:3306/";//连接地址 Connection conn = DriverManager.getConnection(connUrl + databaseName, userName, password); PreparedStatement st = null; Statement stmt = conn.createStatement(); String createTime = readCsv.currTime(); String sql = "create table if NOT EXISTS AutoTest_TotalInterface(id int NOT NULL auto_increment primary key ,terminalType varchar(50) NOT NULL DEFAULT 'App' ," + "excVersion varchar(50),interfaceType varchar(50) ,interfaceName varchar(50)," + "caseTotalNum int,caseSucNum int,excRate varchar(50) ,creatTime varchar(50) NOT NULL DEFAULT '"+createTime+"'," + " UNIQUE INDEX ( terminalType,excVersion,interfaceType,interfaceName,creatTime ) )"; // 创建数据库中的表, int result = stmt.executeUpdate(sql); if (result != -1) { String selectSQL = "SELECT terminalType,excVersion,interfaceType," + "interfaceName,caseTotalNum,caseSucNum," + "ROUND(caseSucNum/caseTotalNum,3) " + "as excRate,creatTime from(" + "SELECT terminalType,excVersion,interfaceType,interfaceName,count(1) " + "as caseTotalNum,sum(ex) as caseSucNum,creatTime from(" + "SELECT terminalType,excVersion,interfaceType,interfaceName," + "creatTime,case excResult " + "when 'true' then 1 when 'false' then 0 end as ex from " + "AutoTest_DetailInterface)m group by " + "terminalType,excVersion,interfaceType,interfaceName,creatTime)n"; sql = "insert into AutoTest_TotalInterface(" + "terminalType,excVersion,interfaceType,interfaceName,caseTotalNum,caseSucNum," + "excRate,creatTime) "+selectSQL+" ON DUPLICATE KEY UPDATE caseTotalNum=VALUES(caseTotalNum),caseSucNum=VALUES(caseSucNum),excRate=VALUES(excRate)"; System.out.println(sql); //执行插入操作 st = conn.prepareStatement(sql); st.executeUpdate(); sql = "SELECT * FROM AutoTest_TotalInterface"; System.out.println(stmt.executeQuery(sql)); ResultSet rs = 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 (Exception e) { e.printStackTrace(); return false; } return true; } 好了,到此结束,当然,importDB的jmx文件也要发生变化(直接调用方法即可),具体如下: import readDB.*; import excFile.*; String path = "D:\\excResult.csv"; int length = readCsv.getTerminalTypeArray(path).size(); log.info("用例数量为:"+length); //循环获取各个参数 for(int i=0;i<length;i++){ String terminalTypeKey = readCsv.getTerminalTypeArray(path).get(i); log.info("获取终端类型:"+terminalTypeKey); String excVersionKey = readCsv.getVersionArray(path).get(i); log.info("获取版本号:"+excVersionKey); String interfaceTypeKey = readCsv.getInterfaceTypeArray(path).get(i); log.info("获取接口类型:"+interfaceTypeKey); String interfaceNameKey = readCsv.getInterfaceNameArray(path).get(i); log.info("获取接口名称:"+interfaceNameKey); String caseNameKey = readCsv.getCaseNameArray(path).get(i); log.info("获取用例名称:"+caseNameKey); String excResultKey = readCsv.getCaseIsPassArray(path).get(i); log.info("获取执行结果:"+excResultKey); readCsv.insertDetailDB(terminalTypeKey, excVersionKey, interfaceTypeKey, interfaceNameKey, caseNameKey, excResultKey); } //插入统计表 readCsv.insertTotalDB(); String dir = "D:\\"; String oldname = "excResult.csv"; log.info("获取最新文件名称:"+renFile.currTime()); String newname = "excResult"+renFile.currTime()+".csv"; log.info("获取最新文件名称:"+newname); renFile.renameFile(dir, oldname, newname); |
导入DB数据如下:
原始数据:
导入详细表的数据:
导入统计表数据: