模拟测试环境实现随机造数功能
背景:需要实现如下功能
1,提供自动测试接口功能,每天运行
2,随机生成数据组成请求参数请求各个接口(某些参数必须要从数据库读取,随机生成无法实现)
3,提供页面,实现只输入json字符串,系统,请求服务ID,请求类型,便能够请求各类接口
4,页面请求的参数如果与测试用例的参数一致,返回测试用例的预测结果以及接口返回的结果,不一致,便单独返回接口的请求结果
5,页面能够自动把json数据格式化以及把测试用例的结果与接口返回结果进行对比
==============================================================================
由于之前在服务器上搭建了jenkins+tomcat+zookeeper+dubbo的环境,现已经部署了所有的线上运行项目,由此有如下思路
思路:
1,需要单独部署一个独立项目
2,独立项目提供页面输入请求参数
3,需要提供定时任务
4,需要随机生成数据
5,需要连接数据库
基于此采取如下方案
方案:
1,选用springboot+mybatis+oracle+druid(如果需要再增加redis作为缓存)
2,前端采用html+css+js+jquery+bootstrap3+ajax(页面没有实现自适应)
===============================================================================
前端页面样式
备注:此为一个页面
============================================================================================
页面代码:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <title>Jfpointscore-Test</title> <!-- Bootstrap --> <link href="css/bootstrap.css" rel="stylesheet"> <link href="css/style.css" rel="stylesheet"> <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) --> <script src="js/jquery-1.10.1.js"></script> <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 --> <script src="js/bootstrap.js"></script> <script src="js/index.js"></script> </head> <body> <!--容器 --> <div class="container"> <div class="page-header"> <h1>Compare Json<small></small></h1> </div> <div id="request_container" class="jumbotron"> <h4 id="tip_font" class="tip_font">请输入Json数据</h4> <textarea id="input_content" class="request_content"></textarea> <div class="request_container_bottom"> <!--格式化按钮 --> <div id="format_button" class="btn-group format_button" role="group" aria-label="..."> <button id="format_json" type="button" class="btn btn-default" onclick="click_format_json();">格式化</button> </div> <!--系统按钮 --> <div class="blank_button btn"> System: </div> <div id="system_button" class="dropdown system_button"> <button class="btn btn-default dropdown-toggle" type="button" id="system_type" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true" value=""> 请选择请求应用 <span class="caret"></span> </button> <ul class="dropdown-menu" aria-labelledby="system_type"> <li><a href="javascript:void(0)" id="system_jfpointscore-service" onclick='get_system("jfpointscore-service");'>jfpointscore-service</a></li> <li role="separator" class="divider"></li> <li><a href="javascript:void(0)" id="system_jfpointscoreqry-service" onclick='get_system("jfpointscoreqry-service");'>jfpointscoreqry-service</a></li> <li role="separator" class="divider"></li> <li><a href="javascript:void(0)" id="system_jfpointscore-job" onclick='get_system("jfpointscore-job");'>jfpointscore-job</a></li> <li role="separator" class="divider"></li> <li><a href="javascript:void(0)" id="system_jfpointssettle-job" onclick='get_system("jfpointssettle-job");'>jfpointssettle-job</a></li> </ul> </div> <div class="blank_button btn"> RequestType: </div> <!--请求类型按钮 --> <div id="request_button" class="dropdown request_button"> <button class="btn btn-default dropdown-toggle request_button_button" type="button" id="request_type" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true" value=""> 请选择请求类型 <span class="caret"></span> </button> <ul class="dropdown-menu" aria-labelledby="request_type"> <li><a href="javascript:void(0)" id="system_jfpointscore-service" onclick='get_request_type("biz");'>biz</a></li> <li role="separator" class="divider"></li> <li><a href="javascript:void(0)" id="system_jfpointscoreqry-service" onclick='get_request_type("dubbo");'>dubbo</a></li> <!-- <li><a href="#" onclick='get_system("none");'>reset</a></li> --> </ul> </div> <div class="blank_button btn"> ServiceId: </div> <!--serviceId --> <div class="form-group service_id" id="service_id"> <input id="serviceId" type="text" class="form-control" placeholder="ServiceId"> </div> <!--格式化按钮 --> <div id="submit_button" class="btn-group submit_button" role="group" aria-label="..."> <button id="submit_request" type="button" class="btn btn-default" onclick="submit_request();">提交</button> </div> </div> </div> <div id="response_container" class="jumbotron response_container"> <div id="response_container_left" class="response_container_left"> <h4 id="tip_font_response" class="tip_font_response">测试用例参数</h4> <textarea id="out_content_left" class="response_content_left" readonly="readonly"></textarea> </div> <div id="response_container_middle" class="response_container_middle"> <!--格式化按钮 --> <div id="response_format_button" class="btn-group response_format_button" role="group" aria-label="..."> <button id="response_format_json" type="button" class="btn btn-default response_format_json " onclick="format_result();">格式化</button> </div> </div> <div id="response_container_right" class="response_container_right"> <h4 id="tip_font_response" class="tip_font_response">系统返回参数</h4> <textarea id="out_content_right" class="response_content_right" readonly="readonly"></textarea> </div> <!--格式化按钮 --> <div id="backup_format_button" class="btn-group backup_response_format_json" role="group" aria-label="..."> <button id="backup_response_format_json" type="button" class="btn btn-default" onclick="format_result();">格式化</button> </div> </div> </div> </body> </html>
=====================================================================
js代码:
//监听textarea的内容 window.onload = function () { //获取文本内容和长度函数 function txtCount(el) { var val = el.value; var eLen = val.length; return eLen; } var content = "请输入Json数据"; var el = document.getElementById('input_content'); el.addEventListener('input',function () { var len = txtCount(this); // 调用函数 if(len == 0){ document.getElementById('tip_font').innerHTML = content; $("#tip_font").css("color","black"); } }); el.addEventListener('propertychange',function () {//兼容IE var len = txtCount(this); // 调用函数 if(len == 0){ document.getElementById('tip_font').innerHTML = content; $("#tip_font").css("color","black"); } }); // $("#out_content_left").text("2222222"); // $("#out_content_right").text("11111111"); } /*格式化json数据*/ function formatJson(){ var flag = false; var text = $("#input_content").val(); //获取json格式内容 var result = text; if(is_json(text)){ result = JSON.stringify(JSON.parse(text), null, 4);//将字符串转换成json对象 $("#tip_font").text("Json数据格式正确"); $("#tip_font").css("color","green"); flag = true; }else{ $("#tip_font").text("Json数据格式错误"); $("#tip_font").css("color","red"); } $("#input_content").val(result); return flag; } /*格式化json内容*/ function click_format_json(){ formatJson(); } /*判断是否是json格式*/ function is_json(str) { var flag = false; if (typeof str == 'string') { try { var obj = JSON.parse(str); if(typeof obj == 'object' && obj ){ flag = true; }else{ flag = false; } } catch(e) { console.log('error:'+str+'!!!'+e); flag = false; } }else{ console.log('It is not a string!'); flag = false; } return flag; } /**获取选择的系统*/ function get_system(systemName){ console.log("传入的系统为"+systemName); //var text = $("#system_"+systemName).html(); var text = systemName; console.log(text); $("#system_type").val(systemName);//button里的值 $("#system_type").text(systemName); } /**获取选择的请求类型*/ function get_request_type(requestType){ console.log("传入的请求类型为"+requestType); //var text = $("#system_"+systemName).html(); var text = requestType; console.log(text); $("#request_type").val(requestType); $("#request_type").text(requestType+""); } /**提交按钮*/ function submit_request(){ var flag = false; var json = $("#input_content").val(); var system = $("#system_type").text();//button显示的内容 var systemValue = $("#system_type").val();//button里的值 var requestType = $("#request_type").text();//button显示的内容 var requestTypeValue = $("#request_type").val();//button里的值 var serviceId = $("#serviceId").val(); console.log(json); console.log(systemValue); console.log(requestTypeValue); console.log(serviceId); if(json.length == 0 || systemValue.length == 0 || requestTypeValue.length == 0 || serviceId.length == 0){ $("#tip_font").css("color","red"); if(json.length == 0){ $("#tip_font").text("json数据不可以为空!"); return; }else{ if(is_json()){ $("#tip_font").text(""); }else{ $("#tip_font").text("Json数据格式错误"); $("#tip_font").css("color","red"); } } if(systemValue.length == 0){ $("#tip_font").text("请求应用不可以为空!"); return; }else{ $("#tip_font").text(""); } if(requestTypeValue.length == 0){ $("#tip_font").text("请求类型不可以为空!"); return; }else{ $("#tip_font").text(""); } if(serviceId.length == 0){ $("#tip_font").text("serviceId不可以为空!"); return; }else{ $("#tip_font").text(""); } } if(json.length != 0 && systemValue.length != 0 && requestTypeValue.length != 0 && serviceId.length != 0){ flag = true; } if(flag){ if(formatJson()){ $("#tip_font").css("color","black"); timer(5); //请求后台 request_admin(); } } } /*按钮禁用5秒*/ function timer(time) { var btn = $("#submit_request"); btn.attr("disabled", true); //按钮禁止点击 btn.text(time <= 0 ? "时间参数不正确" : ("" + (time) + "秒后可再次请求")); var hander = setInterval(function() { if (time <= 0) { clearInterval(hander); //清除倒计时 btn.text("提交"); btn.attr("disabled", false); return false; }else { btn.text("" + (time--) + "秒后可再次请求"); } }, 1000); } /*请求后台*/ //后端使用@RequestBody接收请求使用此类 function request_admin(){ var url = "/jfpointscore-test/action"; $.ajax({ type: "post", url: url, data:JSON.stringify(get_json_data()), cache: false, async : true, dataType: "json", contentType: "application/json", success: function (data ,textStatus, jqXHR) { if("success"==data.status){ console.log("合法!"); console.log("===============测试用例结果如下=================="); console.log(JSON.stringify(data.resultTest)); console.log("===============请求参数结果如下=================="); console.log(JSON.stringify(data.result)); display_result_to_test(data.resultTest); display_result_to_result(data.result); return true; }else{ console.log("不合法!错误信息如下:"+data.errorMsg); return false; } }, error:function (XMLHttpRequest, textStatus, errorThrown) { console.log("请求失败!"); } }); } /*请求后台*/ //后端如果使用HttpServletRequest接收请求,使用req.getParameter("system")获取参数,就用此类写法 function request_admin_http(){ var url = "/jfpointscore-test/action"; var json = $("#input_content").val(); var system = $("#system_type").text();//button显示的内容 var systemValue = $("#system_type").val();//button里的值 var requestType = $("#request_type").text();//button显示的内容 var requestTypeValue = $("#request_type").val();//button里的值 var serviceId = $("#serviceId").val(); $.ajax({ type: "post", url: url, data: {"system":systemValue,"requestType":requestTypeValue,"serviceId":serviceId,"json":json}, cache: false, async : true, dataType: "json", success: function (data ,textStatus, jqXHR) { if("true"==data.flag){ console.log("合法!"); return true; }else{ console.log("不合法!错误信息如下:"+data.errorMsg); return false; } }, error:function (XMLHttpRequest, textStatus, errorThrown) { console.log("请求失败!"); } }); } /*组装json数据*/ function get_json_data() { var json = $("#input_content").val(); var system = $("#system_type").text();//button显示的内容 var systemValue = $("#system_type").val();//button里的值 var requestType = $("#request_type").text();//button显示的内容 var requestTypeValue = $("#request_type").val();//button里的值 var serviceId = $("#serviceId").val(); var jsonResult = { "system": systemValue, "requestType": requestTypeValue, "serviceId": serviceId, "json": json } return jsonResult; } //展示测试用例结果 function display_result_to_test(data){ var result = null; if(data == null || data == ""){ result = "暂无测试用例数据!" ; }else{ result = JSON.stringify(data); } $("#out_content_left").text(result); } //展示请求数据真实返回结果 function display_result_to_result(data){ var result = null; if(data == null || data == ""){ result = "返回数据异常!" ; }else{ result = JSON.stringify(data); } $("#out_content_right").text(result); } //返回结果格式化 function format_result(){ var test_result = $("#out_content_left").text(); var result = $("#out_content_right").text(); if(test_result != null && test_result != "" && test_result != "暂无测试用例数据!"){ test_result = JSON.stringify(JSON.parse(test_result), null, 4);//将字符串转换成json对象 } if(result != null && result != "" && result != "返回数据异常!"){ result = JSON.stringify(JSON.parse(result), null, 4);//将字符串转换成json对象 } $("#out_content_left").text(test_result); $("#out_content_right").text(result); }
============================================================
css代码
/* 容器 */ .container{ height:1200px; color:black; /* outline:1px solid black; */ width:100%; } .jumbotron{ height:40%; } /* 请求json内容框 */ .request_content{ width:100%; height:80%; color:blue; } .request_container_bottom{ width:100%; margin:0 auto; } .tip_font{ color:black; } /* 格式化按钮 */ .format_button{ float:left; } /* 系统下拉按钮 */ .system_button{ float:left; margin-left:-10px; } /* 请求类型下拉按钮 */ .request_button{ float:left; margin-left:-10px; } /*提示 */ .blank_button{ margin-left:20px; float:left; text-align:center; line-height:20px; disabled:disabled; } /* ServiceId */ .service_id{ float:left; margin-left:-10px; width:30%; } /*提交按钮 */ .submit_button{ float:left; margin-left:20px; } /************************************/ /*结果返回容器*/ .response_container{ height:50%; } .response_container_left{ width:45%; height:90%; /* outline:1px solid black; */ float:left; } .response_container_right{ width:45%; height:90%; /* outline:1px solid black; */ float:left; } .response_container_middle{ float:left; width:10%; height:100%; } .response_content_left{ width:100%; height:100%; color:blue; } .response_content_right{ width:100%; height:100%; color:blue; } /*格式化按钮*/ .response_format_button{ margin:200% 20%; } /*备用按钮*/ .backup_response_format_json{ float:right; display:none; position:absolute; right:5%; } textarea:focus{ outline: none; } @media only screen and (max-width: 1150px){ .response_container_middle{display:none} .backup_response_format_json{display:block;} }
=======================================================================
JAVA代码部分:
由于代码无法从公司带出,所以只提供思路
1,springboot的整合请自行百度
2,关于测试应用接口的定时任务,本人采取的做法是100接口就有100个定时任务
3,随机生成数据问题
100个接口对应100个不同的实体类,那么怎么做呢?提供一个通用的方法,使用Random类随机生成各种类型的数据(能解决大部分的接口,特殊接口特殊对待)。
jdk有方法能够遍历每一个class里的属性,方法,属性的类型等,写一个方法去遍历传递进来的类,进行匹配,一般的参数都逃不过12种数据类型
byte,short,int,long,float,double,boolean,char,string,Integer,Date,Bigdecimal这12种。
4,关于每一个接口的参数字段有必传和非必传的问题
本人提供3种做法
1,在resources目录下生成json文件,里面存放json数据,每一个接口对应一个json文件,json文件里的key是类的属性,value是Y和N,Y代表必传,N代表非必传
每个定时任务读取对应的json文件,然后用随机的Random类生成boolean做为if条件
2,自定义注解,在每个类的属性上都定义这个注解,里面的值就是Y和N
3,使用StringTemplate模板
=================================================================================