zoukankan      html  css  js  c++  java
  • 基于Java+HttpClient+TestNG的接口自动化测试框架(十)------ 测试步骤过程中的数据处理

      我们在做接口自动化测试的时候,首先先要想想接口的请求是怎样完成的,或者说,单个的接口请求有哪些要素。以及一些不同请求的不同应对方法。

      简单来说,一个接口测试有几个要素。我们需要挨个的罗列出来。

      0)是否运行该测试。(有个开关控制)

      1)请求的url。(必须的)

      2)请求的header。--------这个来说有点点复杂。一些常用的请求头内容还比较好说。但是,当Content-Type的值有变化的时候,参数需要进行分别的处理。另外,还有一种就是认证需要加Token的情形。

      3)请求参数。--------通常来说,这部分也是有些复杂的,请求参数会多种多样。当然,目前绝大多数的情况还都是Json和文件。

      4)请求结果的判定--------返回Json字符串的判定。某些请求会出现图片和文件的下载,这些也都是要注意的。

      5)请求结果的转存及后续参数的提供--------具体的处理在前面已经讲过。

      对于以上的要素,我们可以设计一个Excel表格来进行管理,罗列出我们需要的要素。然后通过读取该Excel,来自动的进行接口的请求,以及返回内容的判定,从而达到自动化测试的目的。

     

       对于上面的形式,童鞋们可能有很多疑问。这里不展开说,只是提供一种参考。这种表格,我姑且称之为case表格。这里,我简单描述一下这个表格。首先,我们把Excel的内容进行读入,

    run这一列代表是否运行这条测试(可以用Y/N来控制)。

    desc这一列代表本条接口测试的说明,即输入哪个部分,干什么的。

    method这个是请求方法,就是我们通常说的get/post什么的。

    url这个是具体请求的链接,前面不加主机名。因为这个主机名我们可以在xml文件中进行配置。

    header这个就是请求头的信息,在xml中我还会写一个public Headers的公用请求头信息。这里其实是为了碰到不同的情况。比如:我们N个请求中如果都是用到Content-Type:application/json,那么我们可以写在公用的请求头中。但是,如果某个请求需要单独加token来访问,这个时候请求头上可能要带上token的信息。这样的话,就可以在Excel文件中,这一条请求的header中指定这个token。例如:{"Authorization":"Bearer ${sys_token}"}。

    param这个是参数,即访问时需要增加的参数。当然,这里也可以指定上传的文件。----关于上传文件,我们之后再详细讨论。

    contains这个是我自己自定义的,因为有的时候只需要直接判定返回的内容中是否包含某段内容。如果是这样的判定(即不考虑jsonpath),就设定为Y,反之为N。

    verify为判定内容。如果前面是Y,则输入具体需要判定的内容。如果是N,则需要按照JSONPath的路径来指定判定的值。

    save这个为存储变量来用的。假如该条请求的某个返回信息,是下面某个请求需要的,就可以按照这个方式来处理。

      依照上面的描述,在进行Excel读取的时候,我们需要将各个部分的信息先读取出来,并最终按照一定的形式,提供一个List,这个List可以看作是对每行Excel需要进行的接口请求,读取并处理了数据。(这里说的有点抽象,请参考以下的代码)

    package utils;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.math.BigDecimal;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.CellType;
    import org.apache.poi.ss.usermodel.Row;
    import org.apache.poi.ss.usermodel.Sheet;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    
    import com.alibaba.fastjson.JSONObject;
    
    public class excelUtil {
    
        /**
         * 获取excel表所有sheet数据
         * @param clz
         * @param path
         * @return
         */
        public static <T> List<T> readExcel(Class<T> clz, String path) {
            System.out.println(path);
            if (null == path || "".equals(path)) {
                return null;
            }
            InputStream is;
            Workbook xssfWorkbook;
            try {
                is = new FileInputStream(path);
                if (path.endsWith(".xls")) {
                    xssfWorkbook = new HSSFWorkbook(is);
                } else {
                    xssfWorkbook = new XSSFWorkbook(is);
                }
                is.close();
                int sheetNumber = xssfWorkbook.getNumberOfSheets();
                List<T> allData = new ArrayList<T>();
                for (int i = 0; i < sheetNumber; i++) {
                    allData.addAll(transToObject(clz, xssfWorkbook,
                            xssfWorkbook.getSheetName(i)));
                }
                return allData;
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("转换excel文件失败:" + e.getMessage());
            }
        }
        
        /**
         * 获取excel表指定sheet表数据
         * @param clz
         * @param path
         * @param sheetName
         * @return
         */
        public static <T> List<T> readExcel(Class<T> clz, String path,
                String sheetName) {
            if (null == path || "".equals(path)) {
                return null;
            }
            InputStream is;
            Workbook xssfWorkbook;
            try {
                is = new FileInputStream(path);
                if (path.endsWith(".xls")) {
                    xssfWorkbook = new HSSFWorkbook(is);
                } else {
                    xssfWorkbook = new XSSFWorkbook(is);
                }
                is.close();
                return transToObject(clz, xssfWorkbook, sheetName);
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("转换excel文件失败:" + e.getMessage());
            }
    
        }
    
        private static <T> List<T> transToObject(Class<T> clz,
                Workbook xssfWorkbook, String sheetName)
                throws InstantiationException, IllegalAccessException,
                InvocationTargetException {
            List<T> list = new ArrayList<T>();
            Sheet xssfSheet = xssfWorkbook.getSheet(sheetName);
            Row firstRow = xssfSheet.getRow(0);
            if(null ==firstRow){
                return list;
            }
            List<Object> heads = getRow(firstRow);
            //添加sheetName字段,用于封装至apiDataBean中,与bean中的字段相匹配。
            heads.add("sheetName");
            Map<String, Method> headMethod = getSetMethod(clz, heads);
            for (int rowNum = 1; rowNum <= xssfSheet.getLastRowNum(); rowNum++) {
                try {
                    Row xssfRow = xssfSheet.getRow(rowNum);
                    if (xssfRow == null) {
                        continue;
                    }
                    T t = clz.newInstance();
                    List<Object> data = getRow(xssfRow);
                    //如果发现表数据的列数小于表头的列数,则自动填充为null,最后一位不动,用于添加sheetName数据
                    while(data.size()+1<heads.size()){
                        data.add("");
                    }
                    data.add(sheetName);
                    setValue(t, data, heads, headMethod);
                    list.add(t);
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            return list;
        }
    
        private static Map<String, Method> getSetMethod(Class<?> clz,
                List<Object> heads) {
            Map<String, Method> map = new HashMap<String, Method>();
            Method[] methods = clz.getMethods();
            for (Object head : heads) {
                // boolean find = false;
                for (Method method : methods) {
                    if (method.getName().toLowerCase()
                            .equals("set" + head.toString().toLowerCase())
                            && method.getParameterTypes().length == 1) {
                        map.put(head.toString(), method);
                        // find = true;
                        break;
                    }
                }
                // if (!find) {
                // map.put(head, null);
                // }
            }
            return map;
        }
        
        /*
         * 将具体的值确定。
         */
        private static void setValue(Object obj, List<Object> data,
                List<Object> heads, Map<String, Method> methods)
                throws IllegalArgumentException, IllegalAccessException,
                InvocationTargetException {
            for (Map.Entry<String, Method> entry : methods.entrySet()) {
                Object value = "";
                int dataIndex = heads.indexOf(entry.getKey());
                if (dataIndex < data.size()) {
                    value = data.get(heads.indexOf(entry.getKey()));
                }
                Method method = entry.getValue();
                Class<?> param = method.getParameterTypes()[0];
                if (String.class.equals(param)) {
                    method.invoke(obj, value);
                } else if (Integer.class.equals(param) || int.class.equals(param)) {
                    if(value.toString()==""){
                        value=0;
                    }
                    method.invoke(obj, new BigDecimal(value.toString()).intValue());
                } else if (Long.class.equals(param) || long.class.equals(param)) {    
                    if(value.toString()==""){
                        value=0;
                    }
                    method.invoke(obj, new BigDecimal(value.toString()).longValue());
                } else if (Short.class.equals(param) || short.class.equals(param)) {
                    if(value.toString()==""){
                        value=0;
                    }
                    method.invoke(obj, new BigDecimal(value.toString()).shortValue());
                } else if (Boolean.class.equals(param)
                        || boolean.class.equals(param)) {
                    method.invoke(obj, Boolean.valueOf(value.toString())
                            || value.toString().toLowerCase().equals("y"));
                } else if (JSONObject.class.equals(param)
                        || JSONObject.class.equals(param)) {
                    method.invoke(obj, JSONObject.parseObject(value.toString()));
                }else {
                    // Date
                    method.invoke(obj, value);
                }
            }
        }
    
        private static List<Object> getRow(Row xssfRow) {
            List<Object> cells = new ArrayList<Object>();
            if (xssfRow != null) {
                for (short cellNum = 0; cellNum < xssfRow.getLastCellNum(); cellNum++) {
                    Cell xssfCell = xssfRow.getCell(cellNum);
                    cells.add(getValue(xssfCell));
                }
            }
            return cells;
        }
    
        private static String getValue(Cell cell) {
            if (null == cell) {
                return "";            
            } else if (cell.getCellType() == CellType.BOOLEAN) {
                // 返回布尔类型的值
                return String.valueOf(cell.getBooleanCellValue());
            } else if (cell.getCellType() == CellType.NUMERIC) {
                // 返回数值类型的值
                return String.valueOf(cell.getNumericCellValue());
            } else {
                // 返回字符串类型的值
                return String.valueOf(cell.getStringCellValue());
            }
        }
    }

      基于上面的Excel读取,这里我们做成一个TestBase的类,这个类确定了各个参数的数据应该如何处理,并在最终的接口请求中,以何种形式来工作。部分代码在之前的篇章中已经进行过较为详细的说明,这里不再赘述,只为参考。请参见:基于Java+HttpClient+TestNG的接口自动化测试框架(四)-------参数存取处理


    import java.nio.file.Paths;
    import
    java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.dom4j.DocumentException; import org.testng.Assert; import com.alibaba.fastjson.JSONPath; import bean.ApiDataBean; import bean.baseBean; import utils.assertUtil; import utils.excelUtil; import utils.functionUtil; import utils.reportUtil; import utils.stringUtil; public class TestBase { /** * 公共参数数据池(全局可用) */ private static Map<String, String> saveDatas = new HashMap<String, String>(); /** * 替换符,如果数据中包含“${}”则会被替换成公共参数中存储的数据 */ protected Pattern replaceParamPattern = Pattern.compile("\$\{(.*?)\}"); /** * 截取自定义方法正则表达式:__xxx(ooo) */ protected Pattern funPattern = Pattern .compile("__(\w*?)\((([\w\\\/:\.\$]*,?)*)\)"); private static Connection myCon; protected void setSaveDatas(Map<String, String> map) { saveDatas.putAll(map); } /** * 组件预参数(处理__fucn()以及${xxxx}) * * @param apiDataBean * @return */ protected String buildParam(String param) { param = getCommonParam(param); Matcher m = funPattern.matcher(param); while (m.find()) { String funcName = m.group(1); String args = m.group(2); String value; // bodyfile属于特殊情况,不进行匹配,在post请求的时候进行处理 if (functionUtil.isFunction(funcName) && !funcName.equals("bodyfile")) { // 属于函数助手,调用那个函数助手获取。 value = functionUtil.getValue(funcName, args.split(",")); // 解析对应的函数失败 Assert.assertNotNull(value, String.format("解析函数失败:%s。", funcName)); param = stringUtil.replaceFirst(param, m.group(), value); } } return param; } protected void savePreParam(String preParam) { // 通过';'分隔,将参数加入公共参数map中 if (stringUtil.isEmpty(preParam)) { return; } String[] preParamArr = preParam.split(";"); String key, value; for (String prepar : preParamArr) { if (stringUtil.isEmpty(prepar)) { continue; } key = prepar.split("=")[0]; value = prepar.split("=")[1]; reportUtil.log(String.format("存储%s参数,值为:%s。", key, value)); saveDatas.put(key, value); } } /** * 取公共参数 并替换参数,处理${} * @param param * @return */ protected String getCommonParam(String param) { if (stringUtil.isEmpty(param)) { return ""; } Matcher m = replaceParamPattern.matcher(param);// 取公共参数正则 while (m.find()) { String replaceKey = m.group(1); // 如果公共参数池中未能找到对应的值,该用例失败。 Assert.assertNotNull(replaceKey, String.format("格式化参数失败,公共参数中找不到%s。", replaceKey)); String value; // 从公共参数池中获取值 value = getSaveData(replaceKey); //如果值不为空,则将参数替换为公共参数池里读取到的value的值。 if(null != value) { param = param.replace(m.group(), value); //如果值为空,则将参数替换为字符串“null” }else { param = param.replace(m.group(), "null"); } } return param; } /** * 获取公共数据池中的数据 * * @param key * 公共数据的key * @return 对应的value */ protected String getSaveData(String key) { if ("".equals(key) || !saveDatas.containsKey(key)) { return null; } else { return saveDatas.get(key); } } /* * 判定在verfiy里面的结果。 */ protected void verifyResult(String sourceData, String verifyStr, boolean contains) { if (stringUtil.isEmpty(verifyStr)) { return; } //将判定字符串中的定义变量全部换成实际值。 String allVerify = getCommonParam(verifyStr); reportUtil.log("验证数据:" + allVerify); //是否判定包含。 if (contains) { // 验证结果包含 assertUtil.contains(sourceData, allVerify); } else { // 通过';'分隔,通过jsonPath进行一一校验 Pattern pattern = Pattern.compile("([^;]*)=([^;]*)"); Matcher m = pattern.matcher(allVerify.trim()); while (m.find()) { String actualValue = getBuildValue(sourceData, m.group(1)); String exceptValue = getBuildValue(sourceData, m.group(2)); reportUtil.log(String.format("验证结果,实际值为:%s,期待值为:%s", actualValue, exceptValue)); Assert.assertEquals(actualValue, exceptValue, "验证预期结果失败。"); } } } /** * 获取格式化后的值 * * @param sourchJson * @param key * @return */ private String getBuildValue(String sourchJson, String key) { key = key.trim(); Matcher funMatch = funPattern.matcher(key); if (key.startsWith("$.")) {// jsonpath Object x = JSONPath.read(sourchJson, key);
           //为了处理x为空的情况,没有比较好的方法,只能简单处理一下
    if(x == null) { key = (String)x; }else { key = x.toString(); } } else if (funMatch.find()) {
            //处理函数的部分 String args = funMatch.group(2); String[] argArr = args.split(","); for (int index = 0; index < argArr.length; index++) { String arg = argArr[index]; if (arg.startsWith("$.")) { argArr[index] = JSONPath.read(sourchJson, arg).toString(); } } String value = functionUtil.getValue(funMatch.group(1), argArr); key = stringUtil.replaceFirst(key, funMatch.group(), value); } return key; } /** * 提取json串中的值保存至公共池中 * * @param json * 将被提取的json串。 * @param allSave * 所有将被保存的数据:aa=$.jsonpath.AA;bb=$.jsonpath.BB,将$.jsonpath. * AA提取出来的值存放至公共池的aa中,将$.jsonpath.BB提取出来的值存放至公共池的bb中 */ protected void saveResult(String json, String allSave) { if (null == json || "".equals(json) || null == allSave || "".equals(allSave)) { return; } allSave = getCommonParam(allSave); String[] saves = allSave.split(";"); String key, value; for (String save : saves) { Pattern pattern = Pattern.compile("([^;=]*)=([^;]*)"); Matcher m = pattern.matcher(save.trim()); while (m.find()) { key = getBuildValue(json, m.group(1)); value = getBuildValue(json, m.group(2)); reportUtil.log(String.format("存储公共参数 %s值为:%s.", key, value)); saveDatas.put(key, value); } } } /** * 根据配置读取测试用例,可读取多个Excel的多个Sheet * * @param clz * 需要转换的类 * @param excelPaths * 所有excel的路径配置 * @param excelName * 本次需要过滤的excel文件名 * @param sheetName * 本次需要过滤的sheet名 * @return 返回数据 * @throws DocumentException */ protected <T extends baseBean> List<T> readExcelData(Class<T> clz, String[] excelPathArr, String[] sheetNameArr) throws DocumentException { List<T> allExcelData = new ArrayList<T>();// excel文件數組 List<T> temArrayList = new ArrayList<T>(); for (String excelPath : excelPathArr) { File file = Paths.get(System.getProperty("user.dir"), excelPath).toFile(); temArrayList.clear(); if (sheetNameArr.length == 0 || sheetNameArr[0] == "") { temArrayList = excelUtil.readExcel(clz, file.getAbsolutePath()); } else { for (String sheetName : sheetNameArr) { temArrayList.addAll(excelUtil.readExcel(clz, file.getAbsolutePath(), sheetName)); } } temArrayList.forEach((bean) -> { bean.setExcelName(file.getName()); }); allExcelData.addAll(temArrayList); // 将excel数据添加至list } return allExcelData; } /** * 从MySql数据库中读取测试用例 * * @param clz * 需要转换的类 * @param mysqlStr * 所需要执行的sql语句 * @return 返回数据 * @throws Exception */ @SuppressWarnings("unchecked") protected static <T extends ApiDataBean> List<T> readMySql (Class<T> clz,String mysqlStr) throws Exception{ List<T> dataList = new ArrayList<T>(); String mydriver = "com.mysql.jdbc.Driver"; //防止SSL报错信息,在连接之后加上?autoReconnect=true&useSSL=false String myUrl = "jdbc:mysql://localhost:3306/apidata?autoReconnect=true&useSSL=false"; String user = "root"; String password = "123456"; try { Class.forName(mydriver); }catch (Exception e) { e.printStackTrace(); } try { myCon = DriverManager.getConnection(myUrl, user, password); }catch(Exception e){ e.printStackTrace(); } if(myCon == null) { reportUtil.log(myUrl +"----------数据库连接失败"); } try { Statement st1 = myCon.createStatement(); ResultSet rs = st1.executeQuery(mysqlStr); while(rs.next()) { ApiDataBean adb = new ApiDataBean(); adb.setRun(rs.getBoolean("run")); adb.setDesc(rs.getString("desc")); adb.setMethod(rs.getString("method")); adb.setUrl(rs.getString("url")); adb.setHeader(rs.getString("header")); adb.setParam(rs.getString("param")); adb.setContains(rs.getBoolean("contains")); adb.setVerify(rs.getString("verify")); adb.setSave(rs.getString("save")); dataList.add((T) adb); } } catch (SQLException e) { e.printStackTrace(); } return dataList; } }

             在上面的这段代码中,我们可以看到对应每个部分的参数对应的一些处理方式。具体来说,在读取了Excel,并将其存储起来之后,对于请求参数/判定/保存变量参数方面的处理。可以根据实际的需要,在以上的代码中进行扩展。

  • 相关阅读:
    Golang gRPC 入门
    ProtoBuf编解码
    Proto3语法入门
    Golang RPC入门
    Windows用mstsc(远程桌面)远程Ubuntu 12.04时无法显示Ubuntu桌面解决办法
    ABAPSM30配置TCODE
    eclipselog4j.properties配置
    Hadoop—配置3个副本,但是实际只有1个副本。
    Hadoop—org.apache.hadoop.io.nativeio.NativeIO$Windows.access0
    Jmeter BeanShell使用场景
  • 原文地址:https://www.cnblogs.com/generalli2019/p/12930173.html
Copyright © 2011-2022 走看看