httpclient+testng接口自动化框架二次封装Java

倒叙看,最新的在上面。。。。

框架设计见我的另一个博客https://blog.csdn.net/weixin_42498050/article/details/115671411

 

 

开发不规范的地方:(这就是CR的重要性和要求开发编码规范的重要性)

 

1 json返回格式非标准,如

序列号:4 cmo获取mec集群列表,正常返回,返回的datacenterid为uuid->4的接口返回为[{}]

2 返回的参数命名不规范,如

cmo注册,正常返回->1  参数命名为_拼接  datacenter_id

DatacenterID

cmo获取mec集群列表,正常返回,返回的datacenterid为uuid->4 返参又为大写的DatacenterID

 

3 必传参数缺失,接口的Response Code为200(看约定,restful就不正常。普通接口可以返回200,然后json信息告知缺少参数)。接口返回的value都为空-纯空???业界都不这样返回(this is basic knowledge)。

正常来说response code为400 Bad Request或者500 Internal Server Error或者 401 或者别的错误状态码

接口返回为错误状态码和message 如

{
    "code": "0001",
    "msg": "参数无效",
    "data": "BAD_REQUEST"
}

{
    "code": "10002",
    "msg": "参数缺失",
    "data": "BAD_REQUEST"
}

专业版状态码:无论接口文档or开发代码or测试开发自动化代码都有写。现在的公司没有

response status: 200
返回结果: {
 "Mongo_Id": "",
 "id": "",
 "name": "",
 "cluster": "",
 "comment": "",
 "cpu": "",
 "video_type": "",
 "vmcustom_properties": "",
 "run_on": "",
 "run_once": "",
 "vm_ip": ""
}
 

4 接上面,不传必传参数 response code返回又为400了 上面的为200。不统一。。。。 

 



自动化测试过程中对框架的优化记录

ps 感谢前公司同事欣欣,帅气 技术好 很乖 脾气好 性格好 低调,jar包之王,我搜到的jar包都是收费的,欣欣找的都是免费的。没有欣欣 没有框架

 

4月22

token鉴权的2种方式,参考博客

Http请求Authorization认证:传Authorization时选择Basic Auth 填写用户名 密码。此次header不需要填写token、X-Auth-Token,需要填写Project

token登录:header需要填写token、X-Auth-Token、Project。当然必不可少的Content-Type:application/json、Accept:application/json

https://blog.csdn.net/weixin_36260016/article/details/114089891

https://blog.iprac.cn/blogs/55.html

String encoding = DatatypeConverter.printBase64Binary("username:passwd".getBytes("UTF-8"));  //username  password 自行修改  中间":"不可少
getMethod.addRequestHeader(new Header("Authorization", "Basic " + encoding));

 

 

HTTP Header 详解 -Requests部分

Header解释示例
Accept指定客户端能够接收的内容类型Accept: text/plain, text/html
Accept-Charset浏览器可以接受的字符编码集。Accept-Charset: iso-8859-5
Accept-Encoding指定浏览器可以支持的web服务器返回内容压缩编码类型。Accept-Encoding: compress, gzip
AuthorizationHTTP授权的授权证书Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
CookieHTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。Cookie: $Version=1; Skin=new;
Content-Length请求的内容长度Content-Length: 348
Content-Type请求的与实体对应的MIME信息Content-Type: application/x-www-form-urlencoded
Expect请求的特定的服务器行为Expect: 100-continue
From发出请求的用户的EmailFrom: user@email.com
Host指定请求的服务器的域名和端口号Host: www.zcmhi.com

4月21

接口运行结果代码乱码


a���[��
�8N���]��3�K�Eʸe�����a�;(�ĕͱ:9���JUeg�zw^
�\ϵu`:�G�ǏK�1r�<]�4�F'n� ��a��8���/�������-����?��    ��UV
a���[��
�8N���]��3�K�Eʸe�����a�;(�ĕͱ:9���JUeg�zw^
�\ϵu`:�G�ǏK�1r�<]�4�F'n� ��a��8���/�������-����?��    ��UV四月 21, 2021 8:19:03 下午 org.apache.commons.httpclient.HttpMethodBase getResponseBody
a���[��
�8N���]��3�K�Eʸe�����a�;(�ĕͱ:9���JUeg�zw^
�\ϵu`:�G�ǏK�1r�<]�4�F'n� ��a��8���/�������-����?��    ��UV

http协议,就协议本身而言,是一种文本协议。文本协议可读性比较好,但是协议所占的字节较多。目前http服务端响应普遍是json格式。其实是文本格式都行的。xml也算

java栈会接触较多的xml。而且xml的表达性更强,尤其针对字符类型

请求里的Header,Accept为标识返回协议json/xml的参数。在浏览器抓到的请求为xml但是自动化要改为Accept: application/json

长连接的保活机制:  https://blog.csdn.net/qq_22642239/article/details/109024544

 

解决办法:json xml反序列化转换、把xml转化为json格式

postman 浏览器之所以支持xml转json是因为工具本身支持 自动化的话需要自己封装框架

参考 Quickest way to convert XML to JSON in Java 

 https://stackoverflow.com/questions/1823264/quickest-way-to-convert-xml-to-json-in-java

  • 先接收接口的原始格式响应,也就是xml格式响应
  • 将xml 转为java 的对象数据类型,需要下载jar包
  • 如果还需要转换成json对象的话,再序列化一下即可,序列化也有jar包
import org.json.JSONObject;
import org.json.XML;
import org.json.JSONException;

public class Main {

    public static int PRETTY_PRINT_INDENT_FACTOR = 4;
    public static String TEST_XML_STRING =
        "<?xml version=\"1.0\" ?><test attrib=\"moretest\">Turn this to JSON</test>";

    public static void main(String[] args) {
        try {
            JSONObject xmlJSONObj = XML.toJSONObject(TEST_XML_STRING);
            String jsonPrettyPrintString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);
            System.out.println(jsonPrettyPrintString);
        } catch (JSONException je) {
            System.out.println(je.toString());
        }
    }
}

 

4月21

DBModel类

新增mysql8.0连接数据库 对数据库增删改查等方法封装 测试结果写入数据库

public class DBModel {

    /**
     * 本人4月新增开始!!!
     */

    // 建立数据库连接
    public static Connection conn = null;

    public static PreparedStatement pst = null;

    // 创建数据库驱动:mysql之前的版本为5.1.34,driver=com.mysql.jdbc.Driver。8.0.15版本后,driver=com.mysql.cj.jdbc.Driver。多了cj
//        String driver = "com.mysql.jdbc.Driver";
    public static String driver = "com.mysql.cj.jdbc.Driver";
    //数据库IP地址
    public static String ip = "xx";
    //数据库端口
    public static int port = 3306;
    //连接的mysql的数据库名字
    public static String database = "xx";
    // mysql配置时的用户名
    public static String user = "root";
    // mysql配置时的密码
    public static String password = "xx";
    // URL指向要访问得数据库名 database
//        String url = "jdbc:mysql://" + ip + ":" + port + "/" + database + "?useUnicode=true&characterEncoding=utf8";
    public static String url = "jdbc:mysql://" + ip + ":" + port + "/" + database +
            "?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT&useSSL=false";

 
    // 加载jdbc驱动程序
    static {

        try {
            Class.forName("com.mysql.cj.jdbc.Driver").newInstance();

        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    // 建立数据库连接
    public static Connection getConnection() {

        try {
            conn = DriverManager.getConnection(url, user, password);

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return conn;
    }


    // 关闭数据库连接
    public static void closeConnection(ResultSet rst, PreparedStatement pst, Connection conn) throws Exception {
        try {
            if (rst != null) {
                rst.close();
            }
            if (pst != null) {
                pst.close();
            }
            if (conn != null) {
                conn.close();
            }

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }


    /**
     * 对应数据库字段:build_number
     *
     * @param build_number
     * @param job_id
     * @return
     */
    public static String reportAll(String build_number, String job_id, int passed, int failures, int skip, int total) {
        Connection conn = null;
        Statement st = null;
        boolean ret = false;

        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
            try {
                st = conn.createStatement();

//                String sql = "insert into api_job (build_number) values('CMO-3.1-daily_2021-04-19 12:55:09.tar.gz');";
                String sql1 = "insert into api_job (build_number,job_id,passed,failures,skip,total) " +
                        "values('" + build_number + "','" + job_id + "',"
                        + passed + "," + failures + "," + skip + "," + total + ");";
//            String sql = "select * from api_job where build_number=\"CMO-3.1-daily_2021-04-19 10:55:09.tar.gz\";";
                System.out.println("debug-sql==" + sql1);
//                System.out.println("debug-st==" + st);
                // 插入sql,如果插入成功则返回成功的条数,executeUpdate返回结果为int类型
                int issucc1 = st.executeUpdate(sql1);
//                int issucc2 = st.executeUpdate(sql2);
                // execute返回结果为boolean类型
//                boolean issucc = st.execute(sql);
                System.out.println("debug-issucc1==" + issucc1);
//                System.out.println("debug-issucc2==" + issucc2);

                // 以下3行conn的操作不加也行。默认AutoCommit应该是开启的,不用手动commit了。commit是为了一次执行多个SQL用的
                /*
                在connection类中提供了3个控制事务的方法:
                (1) setAutoCommit(Boolean autoCommit):设置是否自动提交事务
                (2) commit();提交事务
                (3) rollback();撤消事务
                参考博客https://www.cnblogs.com/Bonker/p/5417967.html
                innodb锁机制: https://www.cnblogs.com/aipiaoborensheng/p/5767459.html

                如果一个方法里面要执行多个SQL操作,那么开始可以设置不自动提交,然后等多个SQL操作完成了,统一提交,如果出错了就回滚
                 */
//                conn = getConnection();
//                // auto commit 设置为false之后,就不会自动提交,会导致其他索引"锁冲突"的语句阻塞
//                conn.setAutoCommit(false);//在conn = getConnection();后面添加这么一句即可。
//                conn.commit();
//                System.out.print("sql语句执行成功");  //执行能后现实该语句,应该实执行成功了。

                // 正常的话 这里应该返回1
                if (issucc1 > 0) {
                    ret = true;
                } else {
                    ret = false;
                }


            } catch (SQLException ex) {
                ex.printStackTrace();
            }

            System.out.println("debug-ret==" + ret);
//            closeConnection(rs, pst, conn);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                    conn = null;
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            // insert插入数据需要关闭数据连接的代码。查询需要打开
//            session.disconnect();
        }

        return null;

    }

 

同时新增logOpera的工具类

public class logOpera {

    public static int pass(String logname) {
        String filePathAll = "/Users/qa/Desktop/2021/code/APInterfaceFrame/testresult/";

        String countLog = filePathAll + logname;
//        System.out.println("filePath_type=="+filePath_type);
        String[] logs = readTxt(countLog);

        // 顺序:pass fail ignore total
        HashMap<String, String> hm = new HashMap<>();
        int pass;
        int fail;
        int ignore;
        int total;

        for (int i = 0; i < logs.length; i++) {

            // 打印接口返回的数据
//            System.out.println("第【" + i + "】条日志,预发环境pre接口返回response为=======" + response);
//            System.out.println("每次循环的totalCount=="+totalCount);

//            System.out.println("logs==" + logs[i]);

            hm.put("pass", logs[0]);
            hm.put("fail", logs[1]);
            hm.put("ignore", logs[2]);
            hm.put("total", logs[3]);
//            System.out.println("hm=="+hm);
        }

        pass = Integer.parseInt(hm.get("pass"));
        fail = Integer.parseInt(hm.get("fail"));
        ignore = Integer.parseInt(hm.get("ignore"));
        total = Integer.parseInt(hm.get("total"));

//        System.out.println("pass==" + pass);
//        System.out.println("fail==" + fail);
//        System.out.println("jgnore==" + ignore);
//        System.out.println("total==" + total);

        return pass;
    }
 

数据库字段

 

package com.interfaceframe.bg.testcase;


@RunWith(OverrideRunner.class)
public class mecdebug {

    @Test
    @Case(desc = "mecdebug.txt", order = 1)
    public void login() {
        long startTime = System.currentTimeMillis();
        String start_time = TimeTransfer.TimeStamp2Date(startTime);
        System.out.println("所有case开始时间==" + start_time);

        new CaseUtil().executeCase("/mec/mecdebug.txt", "/URL/mectest.txt");

        long endTime = System.currentTimeMillis();
        System.out.println("所以case结束时间==" + TimeTransfer.TimeStamp2Date(endTime));

        /**
         *  插入build_number。在cmo设置中可以看到版本号
         */
        String build_number = "CMO-3.1-daily_" + TimeTransfer.TimeStamp2Date() + ".tar.gz";
//        System.out.println("debug-插入build_number==" + 插入build_number);

        /**
         * 插入job_id唯一id
         */
        String job_id = new RandomJobid().getId();
//        System.out.println("job_id==" + job_id + ",长度为==" + job_id.length());

        /**
         * 插入passed failures errors skip total 各自的用例数
         */
        String logname = "mecdebug_login.txt";
        int passed = logOpera.passed(logname);
        System.err.println("debug-passed=" + passed);

        int failures = logOpera.failures(logname);
        System.err.println("debug-failures=" + failures);

        int errors = 0;
        System.err.println("debug-errors=" + errors);

        int skip = logOpera.skip(logname);
        System.err.println("debug-skip=" + skip);

        int total = logOpera.total(logname);
        System.err.println("debug-total=" + total);

        /**
         * 插入duration_time case运行时长
         */
        float duration_time = endTime - startTime;
        System.err.println("debug-duration_time=" + duration_time);

        /**
         * 整体插入,调用封装的数据库方法
         */
        DBModel.reportAll(build_number, job_id, passed, failures, errors, skip, total, duration_time, start_time);

    }

}

 

效果:testng自动化运行结果插入mysql数据库成功,耶!!!

完整记录如下

 


新增Utils工具类下的RandomJobid

package com.interfaceframe.bg.util;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

public class RandomJobid {

    public String getId() {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        long time = new Long(System.currentTimeMillis()); // 获取当前时间的unix时间戳
        // 截取前10位 2021-04-20 11:44:34
        String format = sdf.format(new Date(time)).substring(0, 10).replaceAll("-", "");
        // 打印为8位数字
//        System.out.println("format==" + format);

        UUID uuid = UUID.randomUUID();
        // 32位的唯一id拼接8位时间,最终返回40位的jobid
        String jobId = uuid.toString().replace("-", "") + format;

        return jobId;
    }


    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            System.out.println("第" + i + "次循环: " + new RandomJobid().getId() + ",长度是:" + new RandomJobid().getId().length());


        }

        //第1次循环:d93d954031ba46f189218b70fb515e81

    }
}

 

CaseUtil类executeCase方法

记录pass fail ignore total的数目,插入数据库用

private String log;
private String logCount;
        log = "testresult/" + genedReportName + "_Result.log"; // 获得调用者的方法名
        logCount = "testresult/" + genedReportName + "Log_log"; // 获得调用者的方法名
        // 4月8开会讨论保留历史日志,加时间戳
//        log = "testresult/" + TimeTransfer.TimeStamp2Date(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss") + "_" + genedReportName + "_Result.log";//
        FileUitl.deleteFile(log);
        FileUitl.deleteFile(logCount);
//记录每个case的log日志
StringBuffer caseStepLog = new StringBuffer();
StringBuffer caseStepLogCount = new StringBuffer();
// 4月新增
int total = pass + fail + ignore;
System.err.println("pass=【" + pass + "】,fail=【" + fail + "】,ignore=【" + ignore + "】,total=【" + total + "】");

caseStepLogCount.append("pass=" + pass + ",fail=" + fail + ",ignore=" + ignore + ",total=" + total); //测试通过
FileUitl.writeFile(logCount, caseStepLogCount.toString());

4月19

如遇

com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.

完整日志

com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:377)
    at com.mysql.jdbc.Util.getInstance(Util.java:360)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:935)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:924)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:870)

解决办法:参考https://blog.csdn.net/zht741322694/article/details/82464024

原框架里的mysql jar包版本是5.1.34 要下载8.0版本的

参考mysql 8.0.15 jar包下载地址/mysql8.0jar包下载地址(mysql官网下载https://jingyan.baidu.com/article/22fe7ced29711e3002617f2c.html) 

或者  https://dev.mysql.com/downloads/file/?id=484819

直接点击No thanks, just start my download. 下载包将直接以zip格式下载到本地,解压就看到了

 

下载完8.0.15jar包,在project structure-Modules-Dependencies-libss(原来的放到这里了)-点击+导入下载的jar包(放到项目目录的lib-/Users/qa/Desktop/2021/code/APInterfaceFrame/libss,再从这里导入加载。单纯的拷贝到libs下面不会生成META-INFO和org文件

加载完成,jar包可以正常展示了。可以展开了


继续执行报错

Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.

如遇

Sun Apr 18 22:29:27 CST 2021 WARN: Caught while disconnecting...

EXCEPTION STACK TRACE:

** BEGIN NESTED EXCEPTION ** 

javax.net.ssl.SSLException
MESSAGE: closing inbound before receiving peer's close_notify

STACKTRACE:

javax.net.ssl.SSLException: closing inbound before receiving peer's close_notify
    at sun.security.ssl.Alert.createSSLException(Alert.java:133)
    at com.interfaceframe.bg.jdbc.mysql.util.DBUtil.main(DBUtil.java:74)


** END NESTED EXCEPTION **

解决办法:参考博客https://blog.csdn.net/qq_34075488/article/details/85106860

在使用spring boot整合jpa时出现上述错误,去网上找了很多资料,按照所述方法试了之后仍报错,但最后发现了一篇文章。在配置文件中,配置连接数据库的url时,加上useSSL=false。如以下格式,注意将数据库名(db_testjpa)改为你自己的数据库名。

spring.datasource.url = jdbc:mysql://localhost:3306/db_testjpa?serverTimezone=GMT%2B8&useSSL=false
 

修改DBUtil类,getConn()方法

参考博客https://blog.csdn.net/weixin_47962780/article/details/106980730  mysql8.0新版本的mysql连接代码、mysql8.0 jdbc连接代码_Mysql8.0的JDBC连接配置

 

    public static Connection getConn() {
        Connection conn = null;

        // 创建数据库驱动:mysql之前的版本为5.1.34,driver=com.mysql.jdbc.Driver。8.0.15版本后,driver=com.mysql.cj.jdbc.Driver。多了cj
//        String driver = "com.mysql.jdbc.Driver";
        String driver = "com.mysql.cj.jdbc.Driver";
        //数据库IP地址
        String ip = "xx.xx.xx.xx";
        //数据库端口
        int port = 3306;
        //连接的mysql的数据库名字
        String database = "database";
        // mysql配置时的用户名
        String user = "root";
        // mysql配置时的密码
        String password = "passwd";
        // URL指向要访问得数据库名 database
//        String url = "jdbc:mysql://" + ip + ":" + port + "/" + database + "?useUnicode=true&characterEncoding=utf8";
        String url = "jdbc:mysql://" + ip + ":" + port + "/" + database +
                "?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT&useSSL=false";
        // 不加useSSL=false会报错javax.net.ssl.SSLException MESSAGE: closing inbound before receiving peer's close_notify
//                "?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";

        try {
            Class.forName(driver);
            // 百度出来的结果:需要增加&useSSL=false,参考博客https://blog.csdn.net/qq_34075488/article/details/85106860
            conn = DriverManager.getConnection(url, user, password);
            System.out.println("conn==" + conn);
//            conn = DriverManager.getConnection(url, user, password);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return conn;
    }

 

 

4月16

CaseUtil类

新增对传参格式的转换 之前为String类型,转化为map方式解析,供get方法调用

Line233-247

                    // 4月16新增把传参String类型转化为map 原来的框架不支持application/text格式的传参 仅支持applicationjson/json类型
                    result = Get.get("get", CombinationGetUrl, getParama(paramContent)); //执行get方法 转换为map
//                            result = Get.get("get", CombinationGetUrl, paramContent); //执行get方法 原有的框架仅支持String类型
                } else {
                    //System.out.println("未加密后的get Url: "+CombinationGetUrl);
//                    result = Post.post("get", inteface_url[1], paramContent); //执行get方法
                    result = Get.get("get", inteface_url[1], getParama(paramContent)); //执行get方法 原有的框架仅支持String类型
                    System.out.println("");
                }

 

// 4月16新增把传参String类型转化为map 原来的框架不支持application/text格式的传参 仅支持applicationjson/json类型
// 之前自己写的httpclient框架支持2种类型
public HashMap<String, String> getParama(String paramContent) {
    HashMap<String, String> map = new HashMap<>();
    if (!TextUtils.isEmpty(paramContent)) {
        String[] arrayParam = paramContent.split("&");
        for (int n = 0; n < arrayParam.length; n++) {
            int index = arrayParam[n].indexOf("=");
            String key = arrayParam[n].substring(0, index);
            String valuue = arrayParam[n].substring(index + 1);
            map.put(key, valuue);
        }
    }
    return map;
}
// 4月16为了兼容自动化运行结果以英文展示写到数据库,框架
      casename_Chinese = caseFields[0].trim(); // 第1行,中文测试用例名称,如登录cmo系统,此值不取,忽略此行,注释
      casename = caseFields[1].trim(); // 第2行,英文测试用例名称,如login cmo system,自动化取此行
      interfaceName = caseFields[2].trim(); // login
      method = caseFields[3].trim();
      paramContent = caseFields[4].trim();
      if (method.contains("get")) {
          check = Integer.parseInt(caseFields[5].trim());
          if (caseFields.length == 7) {
              expectResultString = caseFields[6].trim();
              //System.out.println("get's expectResultString>>>> "+expectResultString);
          } else {
              expectResultString = null;
          }
      }

 

4月13

JSONUtil类

新增返回的value为int类型时,如果自动化case接口结果写null,则只校验key不校验value值 

新增返回的value为String类型时,如果自动化case接口结果写"",则只校验key不校验value值

Line177-181

{
 "page_num": 1,
 "page_size": 10,
 "total": null,    //只校验key不校验value值 
 "data": [{
  "datacenter_id": "",   //只校验key不校验value值 
  "datacenter_name": "mec-49-0412",
  "register_status": 4,
  "is_connect": true
 }]
}

    // 4月12新增返回的value为int类型时,如果自动化case写null,则只校验key不校验value值
} else if (value == null) {
    if (actualResultMap.get(key) == null) {
        System.out.println(key + " 和期望结果的 " + key + "相等!具体返回值请参阅下面:");
        System.out.println("系统返回的结果为 " + key + "=" + actualResultMap.get(key) + "\n"
                + "期望的返回结果为 " + key + "=" + value + "\n");
    } else {
        ++fail;
        System.err.println(key + " 和1期望结果的 " + key + "不相等!具体差异请看下面:*************************请注意");
        String errorlog = "系统返回的结果为 " + key + "=" + actualResultMap.get(key) + "\\n"
                + "期望的返回结果为 " + key + "=" + expectResultMap.get(key) + "\\n";
        System.err.println(errorlog.replaceAll("\\\\n", "\n"));
        Wait.time(100);
        sb.append(errorlog + "\\n");
        isPass = false;
    }

} else {
    //System.out.println(testResultMap.get(key)+" ? "+expectResultMap.get(key).toString().trim());
    // 4月13新增返回的value为String类型时,,如果自动化case写"",则只校验key不校验value值
    if (TextUtils.isEmpty(expectResultMap.get(key).toString().trim())
            || actualResultMap.get(key).toString().trim().equals(expectResultMap.get(key).toString().trim())) {
        System.out.println(key + " 和期望结果的 " + key + "相等!具体返回值请参阅下面:");
        System.out.println("系统返回的结果为 " + key + "=" + actualResultMap.get(key) + "\r\n"
                + "期望的返回结果为 " + key + "=" + expectResultMap.get(key) + "\n");
    } else {

 



4月13

Get类

Line38-132

get请求 此方法均支持text json格式的请求。4月13日新增支持Content-Type为text/html格式
为啥新增?因为原来的框架不支持Content-Type text/html; charset=UTF-8(图上面的那张)。。。只支持Content-Type application/json; charset=utf-8


// 优化前的框架调用这里
public class Get {

    public static String get(String method, String url, String paramter) {

        if (method == null || "".equals(method) || url == null || "".equals(url)) {
            return Var.message1;
        }
        if (Var.get.equals(method)) {
            if (paramter == null || "".equals(paramter)) {
                return HttpXmlClient.get(url);
            } else {
                return HttpXmlClient.get(url, paramter);

            }
        } else if (Var.post.equals(method)) {
            return HttpXmlClient.postJsonStr(url, paramter);
        }
        return Var.message2;
    }


    /**
     * get 请求,只需将变动的参数传入params中即可
     *
     * @param url
     * @param params
     * @return
     */
    public static String requestURL;

    // 优化后的框架调用这里
    // get请求 此方法均支持text json格式的请求。4月13日新增
    // 为啥新增?因为原来的框架不支持Content-Type text/html; charset=UTF-8。。。只支持Content-Type application/json; charset=utf-8
    public static String get(String method, String url, Map<String, String> paramter) {
        try {
            Header header = new Header("Content-type", "application/json");
            String response = "";

            // HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。
            // HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient。
            // 使用HttpClient发送请求、接收响应
            HttpClient httpClient = new HttpClient();

            if (url != null) {
                // NameValuePair是简单名称值对节点类型。多用于Java像url发送Post请求。在发送post请求时用该list来存放参数
//                getParamsList(url_online, params, count);
                // 预发环境value替换线上环境value
                List<NameValuePair> qparams_pre = getParamsList_pre(paramter);
                if (qparams_pre != null && qparams_pre.size() > 0) {
                    String formatParams = EncodingUtil.formUrlEncode(qparams_pre.toArray(new NameValuePair[qparams_pre.size()]),
                            "utf-8");
//                    url = url.indexOf("?") < 0 ? url + "?" + formatParams : url + "&" + formatParams;
                    url = url.indexOf("?") < 0 ? url + "?" + formatParams : url + formatParams;

                }
                requestURL = url;

                System.out.println("日志,预发环境pre请求的url==" + url);
                GetMethod getMethod = new GetMethod(url);
                getMethod.addRequestHeader(header);
            /*if (null != headers) {
                Iterator var8 = headers.entrySet().iterator();

                while (var8.hasNext()) {
                    Map.Entry<String, String> entry = (Map.Entry)var8.next();
                    getMethod.addRequestHeader((String)entry.getKey(), (String)entry.getValue());
                }
            }*/
                //System.out.println(getMethod.getRequestHeader("User-Agent"));

                int statusCode = httpClient.executeMethod(getMethod);
                // 如果请求失败则打印出失败的返回码
                if (statusCode != 200) {
                    System.out.println("第" + statusCode + "日志,预发环境请求出错,错误码为=======" + statusCode);
                    return response;
                }
                response = new String(getMethod.getResponseBody(), "utf-8");

            }
            return response;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }


    // 参数格式化
    private static List<NameValuePair> getParamsList_pre(Map<String, String> paramsMap) {
        if (paramsMap != null && paramsMap.size() != 0) {
            List<NameValuePair> params = new ArrayList();
            Iterator var2 = paramsMap.entrySet().iterator();

            while (var2.hasNext()) {
                Map.Entry<String, String> map = (Map.Entry) var2.next();

                // 预发环境最新版本日志回放,请求参数打开以下if else,注释掉最后一行

                // 参数格式化,commons-httpclient自带的方法NameValuePair会自动将==转为=,还有特殊符号格式化
                // NameValuePair是简单名称值对节点类型。多用于Java像url_test发送Post请求。在发送post请求时用该list来存放参数
                params.add(new NameValuePair(map.getKey() + "", map.getValue() + ""));

//                params.add(new NameValuePair(map.getKey() + "", map.getValue() + ""));
            }

            return params;
        } else {
            return null;
        }
    }
}


4月9

CaseUtil类

Line58-66

昨天开会大家提出的建议保留历史log和report 对框架作出调整 如下

        String genedReportName = getExecuteCaseClassName(stackTraceElement.getClassName()) + "_" + stackTraceElement.getMethodName();

//        log = "testresult/" + stackTraceElement.getClassName() + "_" + stackTraceElement.getMethodName() + "_Result.log"; // 获得调用者的方法名
        // 4月8开会提出的建议,保留历史日志信息,改为追加日期保存
        log = "testresult/" + TimeTransfer.TimeStamp2Date(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss") + "_" + genedReportName + "_Result.log";// 获得调用者的方法名
        // 删除原日志
        FileUitl.deleteFile(log);

//        String writetoHtml = "testresultHtml/" + genedHtmlName + ".html";
        // 4月8开会提出的建议,保留历史报告信息,改为追加日期保存
        String writetoHtml = "testresultHtml/" + TimeTransfer.TimeStamp2Date(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss") + "_" + genedReportName + ".html";

        ReportUtil report = new ReportUtil(writetoHtml);

新增时间戳转换工具类

package com.interfaceframe.bg.util;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class TimeTransfer {
        /**
         * Java将Unix时间戳转换成指定格式日期字符串
         *
         * @param timestampString 时间戳 如:"1616410206";
         * @param formats         要格式化的格式 默认:"yyyy-MM-dd HH:mm:ss";
         * @return 返回结果 如:"2021-03-22 18:50:06";
         */
        public static String TimeStamp2Date(long timestampString, String formats) {

            Long timestamp = timestampString;
            String date = new SimpleDateFormat(formats, Locale.CHINA).format(new Date(timestamp));
            return date;
        }
    }

 



4月8

HttpXmlClient类

Line74-121

发现的问题,实际返回结果一直在变。这种的正常(虚拟机 磁盘 内存 amount ioread iowrite cpu等),只需要校验key  value不校验,改为null

huge_pages 和期望结果的 huge_pages不相等!具体差异请看下面:*************************请注意
系统返回的结果为 huge_pages="[HugePage:{sizeKB='1048576', amount='58'}]"
期望的返回结果为 huge_pages="[HugePage:{sizeKB='1048576', amount='98'}]"

 

debug模式下发现正常的get有参数的请求无法走到下面的方法,原因:原有的框架没有get方法,只支持post方法

// get请求有参数方法
public static String get(String url, String paramter) {
    CloseableHttpClient httpclient = HttpClients.createDefault();
    String body = null;
    System.err.println("GET URL:" + url + paramter);

基于框架调整,新增get方法

package com.interfaceframe.bg.method;

import com.interfaceframe.bg.util.HttpXmlClient;
import com.interfaceframe.bg.var.Var;

public class Get {

   public static String get(String method,String url, String paramter) {
      
      if (method==null || "".equals(method) || url==null || "".equals(url)){
         return Var.message1;
      }
      if (Var.get.equals(method)){
         if (paramter==null||"".equals(paramter)){
            return HttpXmlClient.get(url);
         }else{
            return HttpXmlClient.get(url , paramter);

         }
      }else if (Var.post.equals(method)){
         return HttpXmlClient.postJsonStr(url, paramter);
      }
      return Var.message2;
   }
}


4月6

HttpClient的get post请求参数中带有特殊字符响应400问题

参考博客  https://blog.csdn.net/zhang_m_h/article/details/108882194

分析接口自动化运行结果,发现response status(Response Code)为400 正常为200

分析了半天,请求在浏览器postman均正常返回,最后发现是传的参数有问题,

||mec_id=7492d6db-2778-418d-83a2-746e109172c8&page=1&pageSize=10&sort=boot_time

mec_id的value中含有特殊参数- 

解决办法:优化框架。对get post请求参数解析时增加URLEncode

 

4月1 4月8又修改了下

JSONUtil类

Line27-44

序列号:4 cmo获取mec集群列表,正常返回,返回的datacenterid为uuid->4

报错分析,接口返参奇怪的为数组格式--一般公司不这样返回。。。

[{
    "CMOID": "xx",
    "DatacenterID": "xx",
    "DatacenterName": "xx",
    "RegisterStatus": 4,
    "Version": 0,
    "UpdateTime": "2021-03-29 17:59:11 CST",
    "CreateTime": "2021-03-28 08:28:28 CST",
    "Comment": ""
}]

针对特殊的格式优化框架

    public static Map<String, Object> parseJSON2Map(String jsonStr) {
        /*jsonStr = jsonStr.replaceAll("\n", "").replaceAll("\t", "").replaceAll(" ", "");*/

//        System.out.println("接口返回的原始json parseJSON2Map---->" + jsonStr);
        ListOrderedMap map = new ListOrderedMap();
        
        // 2021.4.新增
        // json数组解析(用数组的比较少,不知道这边RD为啥选取这种格式,百度阿里没见过这种返回)
        // 如果最外层的json为Object数组,则需要去掉json前后的[]为标准的json再去解析,左开右闭,从1开始算
        if (jsonStr.startsWith("[")) {
            jsonStr  = jsonStr.substring(1, jsonStr.length() - 1);
            System.out.println("重新解析后的jsonnew==" + jsonStr);
            JSONObject json = JSONObject.fromObject(jsonStr);
            System.out.println("json==" + json);
            // 再对json最外层解析
            for (Object k : json.keySet()) {
                Object v = json.get(k);
                map.put(k.toString(), v);
            }

            // 标准的json返回格式为{}或者{[{},{}]}
        } else {

            // json最外层解析
            JSONObject json = JSONObject.fromObject(jsonStr);

            for (Object k : json.keySet()) {
                Object v = json.get(k);
                map.put(k.toString(), v);
            }
        }

        return map;
    }

运行结果如下,期望值和实际值打印出来了,4月1调整的框架忘了遍历json,只是转换了json格式。。。嘻嘻

 

如遇401错误,接口header缺少参数

HttpXmlClient类

Line59-67

 

IntelliJ IDEA引入第三方jar包或查看Java源码的时候报decompiled.class file bytecode version:52.0(java 8)错误的解决办法

解决办法:在oracle官网下载最新的jdk 安装

链接如下

https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html

 

 

修改本地配置文件

cd ~

vim .bash_profile

 

 

 

➜  ~ git:(master) ✗ /usr/libexec/java_home

/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home

➜  ~ git:(master) ✗ echo $JAVA_HOME   

/Library/Java/JavaVirtualMachines/jdk1.8.0_281.jdk/Contents/Home

 

java -version

 

修改idea的配置 Project Structure-Platform Settings-SDK 

和Preference-Maven-Importing

之前为1.8.0.101 最新的macOS版本为1.8.0.281,把JAVA_HOME的路径写到这里

 

.class文件这个是反编译出来的文件,提示一下这是在jdk8环境编译的 提示意义 可以忽略。.class文件是编译好的 .java的可以编辑。.class是编译后的文件。类似于.exe文件,可以运行的文件。需要编辑源码不是编辑编译后的文件。其实算是看错文件了,我的错。。。。



3月25

HttpXmlClient类

Line102-115

httpget封装

 

public static String get(String url) { 
    CloseableHttpClient httpclient = HttpClients.createDefault();

    String body = null;   
    System.out.println(url); 
    HttpGet get = new HttpGet(url);
    // header头部封装 根据公司现在的传参结构,需要把token认证放到header。2021年3月25调整
    get.addHeader("Content-type", "application/json; charset=utf-8");
    get.addHeader("X-Auth-Token", "xxtoken");

    body = invoke(httpclient, get);        
    closeHttpClient(httpclient);
    return body.trim(); 
}  

 



3月24

遇到的问题及解决

1. eclipse本身支持junit,macOS升级11.2.1版本bigsur后,eclipse不能安装,提示Failed to create the Java Vitual Machine 参考若干博客无解,原因:Info.plist系统设置为644 无法sudo改为777

https://blog.csdn.net/lizhen5117/article/details/109680225

2.下载testng的依赖包maven仓库

地址 search.maven.org

https://search.maven.org/search?q=a:testng

testng的pom配置正常,但是依赖没下载下来。。。scope这里有的博客是test 有的是compile 但是都不对,监听器无法正常继承--待解决

import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;

 

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>provided</scope>
</dependency>

3. 改用jar包方式,发现

运行单元测试用例的时候报错了,java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing

现在有两个办法解决:

junit版本降到4.10    免费下载地址  http://www.java2s.com/Code/Jar/j/Downloadjunit410jar.htm

或者导入hamcrest-core-1.3.jar   免费下载地址 http://www.java2s.com/Code/Jar/h/Downloadhamcrestcore13jar.htm

网上99.9%的搜索结果都是收费的  提供免费地址  http://www.java2s.com/Code/Jar/h/

参考博客  https://blog.csdn.net/u011954243/article/details/77962329

下载完jar包,在project structure-Modules-Dependencies-libs-点击+导入下载的jar包(放到项目目录的lib-/Users/qa/Desktop/2021/code/APInterfaceFrame/libs,再从这里导入加载。单纯的拷贝到libs下面不会生成META-INFO和org文件)

终于OK了

 

 

待续。。。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东方狱兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值