倒叙看,最新的在上面。。。。
框架设计见我的另一个博客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 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了
待续。。。