前言:由于通过jmeter写的接口自动化木有数据导入和统计分析功能,因此做了二次开发,目的是读取每条case获取接口名称和用例名称,通过获取的case执行结果进行计算,得到详细接口的用例通过率存入DB,并解析出每条case的执行结果存入DB。
流程图如下:
由于包含case的详细数据和接口的统计数据,因此目前暂定2张表。
具体表结构如下:
接口统计表:
用例执行结果详细表:
首先就是如何让jmeter生成一定格式的csv文件。那么问题一:如何把执行结果生成CSV文件?问题二如何生成一定格式的CSV文件?
解决方案问题一:
在"察看结果树"监听器加入存储路径(在文件名写入 路径+文件名):如:
执行jmx文件后会生成一个excResult.csv文件。如:
解决方案步骤二:
满足一定格式就是满足如下格式:
那么如何满足此格式呢?
在jmeter的"察看结果树"监听器Configure配置下取消掉"Save As XML"
好了,到此初步的要求已经达到。
剩下的就是开发脚本,如何读取csv文件并插入DB
代码部分就不上传了,有兴趣的同学可以私聊
上传下如何插入DB吧、数据统计吧
数据统计代码如下:
public static int getSucNum(String path) throws Exception{ CsvUtil util = new CsvUtil(path); int rowNum = util.getRowNum(); int caseNum = rowNum -1; int sucNum = 0; List list = new ArrayList(); for(int i=1;i<rowNum;i++){ String caseName = util.getString(i, 2); String result = util.getString(i, 7); list.add(caseName); list.add(result); if(result.equals("true")==true){ sucNum +=1; } } return sucNum ; } //计算百分比 public static String percent(int sucCaseNum, int allCseNum){ // 创建一个数值格式化对象 NumberFormat numberFormat = NumberFormat.getInstance(); // 设置精确到小数点后2位 numberFormat.setMaximumFractionDigits(2); String result = numberFormat.format((float) sucCaseNum / (float) allCseNum * 100); return result; } public static String getPryKey(String path) throws Exception{ CsvUtil util = new CsvUtil(path); String secTitle = util.getString(1, 2); // System.out.println("获取检查的接口名称:"+secTitle); String subTitle = secTitle.substring(2, secTitle.length()); // System.out.println("获取接口名称:"+subTitle); return subTitle; } //获取插入DB的接口执行结果(通过率) public static String getExcRate(String path) throws Exception{ CsvUtil util = new CsvUtil(path); int rowNum = util.getRowNum(); int caseNum = rowNum -1; int sucNum = 0; for(int i=1;i<rowNum;i++){ String caseResult = util.getString(i, 7); // System.out.println("用例执行结果为:" + caseResult); if(caseResult.equals("true")==true){ sucNum +=1; } } String caseRate = CsvUtil.percent(sucNum, caseNum)+"%"; // System.out.println("用例通过率为:"+caseRate); return caseRate; } //获取插入DB的secordaryTitle&excResult(用例名称)&(true or false) public static void getSecKey(String path) throws Exception{ CsvUtil util = new CsvUtil(path); int rowNum = util.getRowNum(); String caseName = null; for(int i=1;i<rowNum;i++){ caseName = util.getString(i, 2); // System.out.println("用例名称为:"+caseName); String caseResult = util.getString(i, 7); // System.out.println("用例执行结果为:" + caseResult); } }
插入DB的代码:
//插入统计数据 public static boolean insertTotalDB(String primaryTitle,String excVersion,String excTerminal,String excRate,int caseTotalNum,int caseSucNum){ 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 sql = "create table if NOT EXISTS AutoTest_TotalInterface(id int NOT NULL auto_increment primary key ,permaryTitle varchar(255) ,excVersion varchar(255),excTerminal varchar(255) NOT NULL DEFAULT 'App' ,excRate varchar(255) ,caseTotalNum int,caseSucNum int,creatTime timestamp NULL DEFAULT CURRENT_TIMESTAMP )"; // 创建数据库中的表, int result = stmt.executeUpdate(sql); if (result != -1) { sql = "insert into AutoTest_TotalInterface(permaryTitle,excVersion,excTerminal,excRate,caseTotalNum,caseSucNum) values(?,?,?,?,?,?)"; st = conn.prepareStatement(sql); st.setString(1, primaryTitle); st.setString(2, excVersion); st.setString(3, excTerminal); st.setString(4, excRate); st.setInt(5, caseTotalNum); st.setInt(6, caseSucNum); st.executeUpdate(); sql = "SELECT * FROM AutoTest_TotalInterface"; System.out.println(stmt.executeQuery(sql)); ResultSet rs = stmt.executeQuery(sql); System.out.println("id\tprimaryTitle\tsexcVersion\texcTerminal\texcRate\tcaseTotalNum\tcaseSucNum\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)+ "\t" + rs.getString(8)); } } conn.close(); } catch (Exception e) { e.printStackTrace(); return false; } return true; }
有2点说明:读取csv文件乱码问题和读取关闭文件(因为结束后会有备份历史记录,因此必须关闭CSV文件)
乱码问题解决方案不要用FileReader而是InputStreamReader可以指定读取编码
public CsvUtil(String fileName) throws Exception { this.fileName = fileName; //FileReader可能会根据不同的环境造成从CSV读取时乱码 br = new BufferedReader(new FileReader(fileName)); //解决乱码 br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName),"utf-8")); String stemp; while ((stemp = br.readLine()) != null) { list.add(stemp); } //关闭csv文件 br.close(); }
代码开发基本完成,就差数据备份了,数据备份的代码如下:
package excFile; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; public class renFile { public static boolean renameFile(String path,String oldname,String newname){ File file=new File(path+oldname); if(file.exists()) { file.renameTo(new File(path+newname)); return true; } return false; } public static String currTime(){ SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置日期格式 String now = df.format(new Date());// new Date()为获取当前系统时间 return now; } public static void main(String[] args) { String path = "d:\\"; String oldname = "excResult.csv"; String newname = "excResult"+renFile.currTime()+".csv"; System.out.println("获取当前时间:"+renFile.currTime()); System.out.println("获取最新的名称:"+renFile.renameFile(path, oldname, newname)); } }
恭喜,代码开发基本结束,那么如何被jmeter引用?
把开发的代码export为jar包
如导出jar包名为excDB.jar
由于插入DB要有jdbc依赖包。(mysql-jdbc.jar)
导出后,把2个jar放到jmeter的lib/ext目录下,重启jmeter 即可引用。
如下:在jmeter的beanshell(取样器)工具中写入调用函数脚本即可
import readDB.*; import excFile.*; String excVersion = "9.1.2"; String excTerminal = "App"; String path = "D:\\excResult.csv"; String primaryTitle = CsvUtil.getPryKey(path); log.info("获取接口名称:"+primaryTitle); String excRate = CsvUtil.getExcRate(path); log.info("获取单个接口用例通过率:"+excRate); CsvUtil util = new CsvUtil(path); int rowNum = util.getRowNum(); //log.info("获取行数:"+rowNum); int caseTotalNum = rowNum -1; log.info("获取用例总数:"+caseTotalNum); int caseSucNum = CsvUtil.getSucNum(path); log.info("获取用例通过数量:"+caseSucNum); for(int i=1;i<rowNum;i++){ String secordaryTitle = util.getString(i, 2); log.info("获取用例名称:"+secordaryTitle); String excResult = util.getString(i, 7); log.info("获取用例执行结果:"+excResult); //执行insertDB---详细数据 CsvUtil.insertDetailDB(primaryTitle, secordaryTitle, excVersion, excTerminal, excResult); } ////执行insertDB---统计数据 CsvUtil.insertTotalDB(primaryTitle, excVersion, excTerminal, excRate, caseTotalNum, caseSucNum);
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的jmx文件脚本有几个变量强调下:
①excVersion---版本号(如9.1.0)
②excTerminal---终端类型(如App、网站、M站)
③path---csv文件路径(如d:\excResult.csv)
④dir---csv在哪个目录下(如d:\)
⑤oldname---csv文件名(如excResult.csv)
其中import这2个包名要准确,就是你在开发这些class文件时创建的包名
import readDB.*; import excFile.*;
如:
这样会把存入DB的jmx文件和用例的jmx文件放在一个文件夹下执行,执行循序会根据jmx文件修改时间的正序来执行,因此一定要确保执行case的jmx文件时间要在导入DB的jmx文件前面。
当然也可以把所有按照接口生成的的jmx文件和分别导入DB的jmx文件放在一个文件夹下,但必须渠道入到的CSV文件名不相同,每个对应读取CSV导入DB的jmx文件的指向路径也不同。
基本完成,执行结果就如上面的导入DB的数据一样。
这样整个开发到此结束!!!
还有就是接口名称和用例名称命名规范的问题!
有2个要求:
第一个要求:由于代码做了接口名称获取的规则,是获取第一条接口case名称但不是单纯的获取,是截断了前面2个字符,后面所有字符作为接口名称。
因此要求case输出者在输出接口自动化时第一条case名称命名为“校验XXXX接口”!!这样根据截断规存入DB的接口名称为“XXXX接口”,才符合接口名称命名规范。
第二个要求:jmx文件,一个接口自动化场景输出一个jmx文件,不要在此jmx文件上输出其它的接口case,在这个接口的基础上输出不同场景的case。
否则会把不同接口统计到一个接口中去!
备份的历史记录文件如下:(原文件名+时间戳)。
为什么要renName?
一方面:也是renName的根本原因,因为每次执行的CSV文件,如果在执行接口自动化的jmx文件前不删除,就会把本次的执行结果追加到原csv文件!!!
另一方面:保存历史记录。
最后也就是视图的展现,数据已存入DB,接下来就是读取数据制图即可!