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 |
Authorization | HTTP授权的授权证书 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
Cookie | HTTP请求发送时,会把保存在该请求域名下的所有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 | 发出请求的用户的Email | From: 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 blocke.printStackTrace();} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}}// 建立数据库连接public static Connection getConnection() {try {conn = DriverManager.getConnection(url, user, password);} catch (SQLException e) {// TODO Auto-generated catch blocke.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 exceptione.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.htmlinnodb锁机制: 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语句执行成功"); //执行能后现实该语句,应该实执行成功了。// 正常的话 这里应该返回1if (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 blocke.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 totalHashMap<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:34String format = sdf.format(new Date(time)).substring(0, 10).replaceAll("-", "");// 打印为8位数字
// System.out.println("format==" + format);UUID uuid = UUID.randomUUID();// 32位的唯一id拼接8位时间,最终返回40位的jobidString 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/85106860conn = DriverManager.getConnection(url, user, password);System.out.println("conn==" + conn);
// conn = DriverManager.getConnection(url, user, password);} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SQLException e) {// TODO Auto-generated catch blocke.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(); // loginmethod = 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-8public 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替换线上环境valueList<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了
待续。。。
httpclient+testng接口自动化框架二次封装Java相关推荐
- python小工具封装_python接口自动化(二)——封装需要用到的工具类
封装需要用的工具类: 1.封装读取Excel的工具类,这里选用的是pandas: importpandas as pd path= 'test.xlsx'sheet_name= 'test_data' ...
- python api开发用什么框架_python+requests接口自动化框架
为什么要做接口自动化框架 1.业务与配置的分离 2.数据与程序的分离:数据的变更不影响程序 3.有日志功能,实现无人值守 4.自动发送测试报告 5.不懂编程的测试人员也可以进行测试 正常接口测试的流程 ...
- python 自动化框架打包_python+requests接口自动化框架
为什么要做接口自动化框架 1.业务与配置的分离 2.数据与程序的分离:数据的变更不影响程序 3.有日志功能,实现无人值守 4.自动发送测试报告 5.不懂编程的测试人员也可以进行测试 正常接口测试的流程 ...
- python+requests+pytest 接口自动化框架(一)
目录 一.Pytest详解以及常用的插件安装 二.Pytest默认的测试用例的规则 三.Pytest用例运行方式以及参数 1.命令行模式运行 命令:pytes 2.主函数模式运行 3.基于pytest ...
- python接口自动化实战(框架)_python接口自动化框架实战
python接口测试的原理,就不解释了,百度一大堆. 先看目录,可能这个框架比较简单,但是麻雀虽小五脏俱全. 各个文件夹下的文件如下: 一.理清思路 我这个自动化框架要实现什么 1.从excel里面提 ...
- pytest+yaml设计接口自动化框架过程记录(一步一步记录如何设计,完结撒花),源码提供,视频教程
pytest+yaml设计接口自动化框架过程记录 第三代框架使用教程,该框架比现在这个完善了很多 框架简介 框架运行演示和功能介绍视频 pytest+yaml框架环境配置和使用教程 0.去年也写了一个 ...
- Python语言+pytest框架+allure报告+log日志+yaml文件+mysql断言实现接口自动化框架
目录 前言 实现功能 目录结构 依赖库 安装教程 接口文档 如何创建用例 创建用例步骤 用例中相关字段的介绍 如何发送get请求 如何发送post请求 如何测试上传文件接口 上传文件接口,即需要上传文 ...
- 接口自动化 ------ 快速搭建接口自动化框架
接口自动化框架要搭成什么,里面的内容究竟应该有多少? 这个问题没有标准答案,因为这要取决于你的项目,以及你想要用框架来解决什么问题等多种因素. 当然了,这里也就不展开讨论了,还是回到接口自动化这个点, ...
- 浅谈python+requests接口自动化框架
为什么要做接口自动化框架 1.业务与配置的分离 2.数据与程序的分离:数据的变更不影响程序 3.有日志功能,实现无人值守 4.自动发送测试报告 5.不懂编程的测试人员也可以进行测试 正常接口测试的流程 ...
最新文章
- 微信浏览器跳转页面加载loading效果问题
- c语言英汉互译编程,用C语言编辑简单英汉互译词典.doc
- iOS多线程编程:线程同步总结 NSCondtion
- vue-router思维导图
- 启用了不安全的HTTP方法
- php文件里搜索关键字,php - PHP从文本文件中搜索关键字(Action'),使用该关键字打印所有行,然后计算打印的行数 - SO中文参考 - www.soinside.com...
- 揭秘“21世纪最性感的职业”:数学、编程、沟通和商业技能一个都不能少!...
- 30 FI配置-财务会计-外币评估-准备外币评估的自动过账
- Kafka eagle 安装
- RazorSQL for Mac:查看和编辑二进制数据和图像
- 宝马发布三款新车,2019年将开启最大规模产品攻势...
- OpenCv图像像素操作
- PSpice和Simulink联合仿真笔记(一)SLPS介绍
- Linux系统编程---消息队列
- 金沙滩开发板单片机学习笔记(2)
- shareSdk 新浪微博的登录分享测试
- 2022国产芯片技术创新与市场应用论坛即将召开
- 托福高频真词List10 // 附托福TPO阅读真题
- PAT B1033旧键盘打字
- [nRF51822] 1、一个简单的nRF51822驱动的天马4线SPI-1.77寸LCD彩屏DEMO
热门文章
- Halcon3d 点云计算平面度
- Ai如何使用路径快速制作圆点从大到小效果,有什么好的方法吗
- ZZY.QR 简单二维码生成编辑器
- iOS中UITextField的使用
- 亲测可用!Ubuntu 上PAC VOC数据集划分Python代码实现!
- 数据结构(六)——循环链表
- Broadcast 探究
- python怎么下载pil库_如何在windows下安装Python的PIL库
- 【CVPR】Calibrated RGB-D Salient Object Detection
- 米拓+php.ini_php.ini 配置详细选项