测试平台
测试机器:
当前构建任务名称 | 当前构建任务号 | 当前构建任务状态 | 当前构建任务时长(s) | 当前构建开始时间 | 测试报告地址 |
---|---|---|---|---|---|
本文共 12096 字,大约阅读时间需要 40 分钟。
好久没写博了,今天就瞎唠唠吧
组内有个测试平台,是基于Python2+tornado 框架写的,之前自己维护了一套系统的UIweb自动化代码,现在需要集成进去。这很可能是自己唯一一次基于python开发了,后面组内要换java。
其实开发内容很简单,但是由于之前只写接口、UI这样的自动化,开发没实操过,难点来源于没实操过,
比如:ajax交互、js、轮询等等。反正不懂的就搜,不能搜怎么写代码 /认真脸翻不了墙的就用bing吧,也不错。由于一直没有解决掉如果在vue实现的前端页面下,通过属性赋值这样的操作去上传图片(随着学习vue,希望可以找到解决方法)。
故我一直都是采用的autoit这个window程序去实现的上传图片操作,其实也蛮稳定的。只不过受制于这个程序,我不能将我的UI自动化脚本在Linux上执行,也不能实现无ui的执行。所以呢,综上所述,我只能把我的脚本放在一台执行脚本专用的window机器上执行。
这个前提条件确定下来了,我就得想怎么实现了。为了更好让集成到测试平台里,让我的脚本更具灵活性,我想到了使用selenium-Grid的方式,由linux去执行脚本,然后调用远程的window机器去执行脚本,看起来不错哦。好的,学习grid,
调试grid,一切顺利。直到最后一步需要见证奇迹的时刻,果然。。。失败了,为什么呢?因为当执行到调用autoit的.exe程序时候,是从脚本执行的机器上去找的。wtf, 算了 ,工期将至,先实现一版用起来吧(流下了没有技术的泪水o(╥﹏╥)o)。
这时候我发现了python-jenkins这个第三方库,提供了丰富的API操作Jenkins的方法。
所以我的方案hi:在测试机器上本地启动一个jenkins服务,然后通过python-jenkins去调用jenkins提供的API去运行测试脚本,再jenkin集成allure生成一个漂亮的报告,行得通哦。需求确定:
可以在 case模块跟mark标记里去选择要执行的测试用例
case模块就不用说了,就是这个mark标记,就是pytest框架里的mark标记功能,可以给case上打上你定义的mark,然后执行时候可以指定mark去执行被标记的case,场景比如:你标记出冒烟需要的case,打上smoke然后执行测试:pytest -m smoke 就可以啦可以查看测试机器的状态
因为就一台机器,所以一个人在执行测试的时候,其他人再点击执行就不行啦,只要机器不是空闲的,就把执行测试的按钮置灰。然后列表页展示相关信息,可以查看测试报告
这个就是通过API去获取任务执行情况了,然后把allure报告地址也给返回到前端终于到撸代码的时候了,因为这个平台是用Python的开发框架 tornado+bootstrap实现的,现学现卖吧,咱也不敢问。
页面长啥样呢,咳咳,美观咱就算了,就是个能用就行
# 路由分发urls = [ (r'/ui_ad_mis', AdMisHandler)]
然后去对应的AdMisHandler去写对应的服务就好了
class AdMisHandler(BaseHandler): def get(self): ''' 返回UI测试页面 ''' self.render("html/ui_ad_mis.html")
先看后端
class QueryCase(BaseHandler): def get(self): ''' 查询case模块 :return: case_module 用例模块 case_mark mark标记 ''' server = self.jenkins_server() case_module = server.get_job_info("exec_group_by_module")["actions"][0]["parameterDefinitions"][0]["choices"] case_mark = server.get_job_info("exec_group_by_mark")["actions"][0]["parameterDefinitions"][0]["choices"] self.finish({"case_module": case_module, "case_mark": case_mark})
别忘记去配置url,然后通过这个服务,我们返回case_module,和case_mark,下面就是前端去接收了。我是希望在点击进入页面的时候,这个列表就可以获取到,所以
在点击左侧菜单栏的时候去发起这个请求,query_case()//查询选择框下拉function query_case(){ $.ajax({ type: "Get", async: true, dataType: "json", url: "/query_case", success:function(data){ for (var i=0;i
上面在js文件里定义qurey_case()这个函数。
type: "Get",是get方法;dataType: "json",数据类型是json;url: "/query_case", 这里就会指向你后端url配置里的路径了;success:function(data),当请求成功,data就是后端返回的数据了,然后通过for循环去遍历展示到下拉框里选择模块进行测试:
选择mark进行测试:
看下实现的效果
OK,那么我选中了一个下拉后,点击执行,就要去执行测试了。
同样的套路,前端定义点击事件,去调用js里的 execute_test()函数。html文件
后端
后端通过server.build_job()去执行配置好的任务即可,这里也考虑到了,如果同时选择了module,mark执行后的操作,test_result_info 这个代码有冗余,可以优化掉,暂时先这样了class ExecTestHandler(BaseHandler): ''' 执行测试 :param sel_case_value 选择的case模块 sel_case_value 选择的case标记 ''' def post(self): sel_case_value = self.get_argument("case_module") sel_case_mark = self.get_argument("case_mark") # 当选择了mark,没选择case模块,执行markcase if sel_case_mark != '' and sel_case_value == '': server = self.jenkins_server() current_job_name = "exec_group_by_mark" next_build_number = server.get_job_info(current_job_name)['nextBuildNumber'] server.build_job(current_job_name, {"mark_name": sel_case_mark}) test_result_info = { "code": 0, "desc": "", "current_job_name": current_job_name, "current_build_number": next_build_number, "current_last_build_result": "测试中", } self.finish(test_result_info) elif sel_case_mark == '' and sel_case_value != '': server = self.jenkins_server() if sel_case_value == "all_case": current_job_name = "exec_all_case" next_build_number = server.get_job_info(current_job_name)['nextBuildNumber'] server.build_job(current_job_name) else: current_job_name = "exec_group_by_module" next_build_number = server.get_job_info('exec_group_by_module')['nextBuildNumber'] server.build_job("exec_group_by_module", {"case_name": sel_case_value}) test_result_info = { "code": 0, "desc": "", "current_job_name": current_job_name, "current_build_number": next_build_number, "current_last_build_result": "测试中", } self.finish(test_result_info) elif sel_case_value == '' and sel_case_mark == '': self.finish({"code":-1,"desc":"缺少case模块参数"})
上面就是执行测试的过程了,因为执行测试是需要时间的,不会立即结束,所以返回点数据到前端页面展示,让人知道开始测试啦
效果就是这样5.然后我这边怎么知道测试是否结束了呢?
这里通过前端的轮询请求查询,当执行结束后,把剩下的数据返回前端// 轮询Jenkins执行状态 var timer = null; timer = window.setInterval(query_result, 10000); //10s一次 function query_result() { req_data = { "job_name": data['current_job_name'], "job_number": data['current_build_number'] } $.ajax({ type: "Post", async: true, dataType: "json", data: req_data, url: "/query_result", success: function (data) { //查询结果是"SUCCESS",请求查询构建信息 if (data["result"] == "SUCCESS") { window.clearInterval(timer); $.ajax({ type: "Post", async: true, dataType: "json", url: "/get_job_info", data: req_data, success: function (data) { window.clearInterval(window.setInterval(query_result, 5000)); // console.log("构建成功,查询构建信息"); // console.log(data); document.querySelector("#job_status").textContent = data['current_last_build_result']; document.querySelector("#job_duration").textContent = data['current_job_build_duration']; document.querySelector("#start_time").textContent = data['current_job_build_start_time']; document.querySelector("#test_report>a").textContent = "查看报告"; document.querySelector("#test_report>a").setAttribute("href", data["report_link"]); }, error: function (data) { window.clearInterval(window.setInterval(query_result, 5000)); console.log("查询构建信息失败"); }, })
后端这里有个查询jenkins构建是否完成的服务
class QueryResultHandler(BaseHandler): ''' 查询构建结果 :return build_result ''' def post(self): next_build_number = int(self.get_argument('job_number')) current_job_name = self.get_argument('job_name') server = self.jenkins_server() job_last_build_result = server.get_build_info(current_job_name, next_build_number)['result'] # 任务最新一次构建结果 build_result = {"result": job_last_build_result} self.finish(build_result)
当构建完成后,去调用另一个返回剩下信息和测试报告地址的服务(有个解析控制台log的方法,暂时还没写,不急,哈哈)
class ReturnJobInfoHandler(BaseHandler): ''' 构建完成后,返回信息 :return test_result_info ''' def parse_jenkins_console_log(self): ''' 解析Jenkins控制台的log输出 :return: test_time 测试时长 ''' pass def post(self): next_build_number = int(self.get_argument('job_number')) current_job_name = self.get_argument('job_name') server = self.jenkins_server() for i in range(0, 100, 1): job_build_duration = (server.get_build_info(current_job_name, next_build_number)['duration']) / 1000 if job_build_duration == 0: sleep(2) job_build_duration i += 1 new_job_build_duration = job_build_duration time_stamp = server.get_build_info(current_job_name, next_build_number)['timestamp'] / 1000 current_job_build_start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time_stamp)) # 构建开始日期 report_link = "http://xxxx:9090/job/%s/%s/allure" % (current_job_name, next_build_number) test_result_info = { "current_last_build_result": "测试完成", "current_job_build_duration": new_job_build_duration, "current_job_build_start_time": current_job_build_start_time, "report_link": report_link } self.finish(test_result_info)
OK 测试结束后,页面就可以看到完成的信息了
点击查看报告还是比较好看的报告。//轮询测试机器执行任务状态var timer = null;timer = window.setInterval(query_running_build,3000);function query_running_build(){ $.ajax({ type: "Get", async: true, dataType: "json", url: "/query_running_build", success:function(data){ document.querySelector("#running_build_status").textContent = data["running_status"]; if (data["running_status"] === "空闲中"){ document.querySelector("#running_build_status").setAttribute("style", "color: #00be67"); document.querySelector("#exec_test").disabled=false; } else{ document.querySelector("#running_build_status").setAttribute("style", "color: red"); document.querySelector("#exec_test").disabled=true; } }, error:function(data){ console.log("获取机器执行状态失败,停止轮询") window.clearInterval(window.setInterval(query_running_build,3000)); } })}
后端对应的服务
class QueryRunningBuild(BaseHandler): def get(self): ''' 返回是否有构建任务 :return: running_status: 构建中,空闲中 ''' server = self.jenkins_server() running_build = server.get_running_builds() if len(running_build) == 0: run_status = {"running_status": "空闲中"} else: run_status = {"running_status": "构建中"} self.finish(run_status)
差不多就是这些吧,附上
python-jenkins的地址:后面再开发可能就是用java springboot +vue 去维护我们新的平台了。那时候应该有新的收获吧。
以上内容,如果有可以改进或者改错的地方,欢迎指出转载地址:http://cwkfz.baihongyu.com/