【airtest】airtest学习汇总--进阶篇:批量运行脚本、汇总报告、导出报告
ps:该文章是进阶篇,如果对airtest没有任何了解的,可以先看下【入门篇】https://www.cnblogs.com/zhangxue521/p/14874848.html
然后再看本章内容
一、项目目录
二、各文件说明
1、air_case。需要执行的脚本air文件,例如login.air。后续直接添加该文件即可,其他的文件都不用动
2、export_log。该文件夹自动生成,是自动导出的日志,发给其他人的时候,直接发送该包,日志中的路径用的相对路径。可以直接找到文件
3、log。该文件夹自动生成,是运行中产生的log.txt文件
4、my_runner.py。运行case的启动器,整个demo的入口文件。【一般报错的都是这个文件】已经将这个文件改成使用导出模板的方式
5、report.py。生成报告入口,但是my_runner.py已经运行完case,生成了报告,无需再执行此文件
6、summary_template.html。汇总报告的模版,执行完case汇总的报告是根据该文件的样式产生的。
7、util.py。工具
三、运行方式
python3 my_runner.py
四、 说明
需要连接设备的直接在my_runner.py执行脚本中,添加devices即可。
怎么获取device数据,稍后更新airtest的小白教程。
五、各文件源码
my_runner.py文件
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # @Time : 2020-02-23 13:33 4 # @Author : zhangxue 5 # @File : my_runner1.py 6 # @Desc : 7 #!/usr/bin/env python 8 # -*- coding: utf-8 -*- 9 from airtest.cli.runner import AirtestCase, run_script 10 from argparse import * 11 import airtest.report.report as report 12 import jinja2 13 import shutil 14 import os 15 import io 16 from lib import Send_email 17 import os.path 18 19 class CustomAirtestCase(AirtestCase): 20 # @classmethod 21 # def setUpClass(cls): 22 # super(CustomAirtestCase,cls).setUpClass() 23 24 25 def setUp(self): 26 print("custom setup") 27 super(CustomAirtestCase, self).setUp() 28 29 def tearDown(self): 30 print("custom tearDown") 31 super(CustomAirtestCase, self).setUp() 32 33 def run_air(self, root_dir='', device=[], scriptname='air_case'): 34 # 用例目录 35 # script_path = root_dir + "/" + scriptname 36 script_path = os.path.join(root_dir, scriptname) 37 # 聚合结果 38 results = [] 39 # 创建log文件 40 # root_log = root_dir + '/' + 'log' 41 root_log = os.path.join(root_dir, 'log') 42 if os.path.isdir(root_log): 43 shutil.rmtree(root_log) 44 else: 45 os.makedirs(root_log) 46 print(str(root_log) + ' is created') 47 48 # 创建export_log文件 49 # export_log = root_dir + '/' + 'export_log' 50 export_log = os.path.join(root_dir, 'export_log') 51 if os.path.isdir(export_log): 52 shutil.rmtree(export_log) 53 else: 54 os.makedirs(export_log) 55 print(str(export_log) + ' is created') 56 57 for f in os.listdir(script_path): 58 if f.endswith(".air"): 59 # f为.air案例名称:login.air 60 airName = f 61 script = os.path.join(script_path, f) 62 # airName_path为.air的全路径/Users/zhangxue/Documents/study/airtest_fppui/air_case/login.air 63 print("当前运行脚本路径:" + str(script)) 64 # 日志存放路径和名称:/Users/zhangxue/Documents/study/airtest_fppui/log/login/log.html 65 log = os.path.join(root_dir, 'log', airName.replace('.air', '')) 66 print("log路径:" + str(log)) 67 if os.path.isdir(log): 68 shutil.rmtree(log) 69 else: 70 os.makedirs(log) 71 print(str(log) + ' is created') 72 # global args该行代码注意,下载的不同的airtest版本,可能参数不太一样,缺少参数直接添加对应的值即可 73 args = Namespace(device=device, log=log, recording=None, script=script, compress=1, no_image='') 74 try: 75 run_script(args, AirtestCase) 76 except: 77 pass 78 finally: 79 # export_output_file = os.path.join(export_log + "/" + airName.replace('.air', '.log') + '/log.html') 80 export_output_file = os.path.join(export_log, airName.replace('.air', '.log'), 'log.html') 81 rpt = report.LogToHtml(script_root=script, log_root=log, export_dir=export_log) 82 rpt.report("log_template.html", output_file=export_output_file) 83 result = {} 84 result["name"] = airName.replace('.air', '') 85 result["result"] = rpt.test_result 86 results.append(result) 87 88 # 生成聚合报告 89 env = jinja2.Environment( 90 loader=jinja2.FileSystemLoader(root_dir), 91 extensions=(), 92 autoescape=True 93 ) 94 template = env.get_template("summary_template.html", root_dir) 95 html = template.render({"results": results}) 96 output_file = os.path.join(export_log, "summary.html") 97 with io.open(output_file, 'w', encoding="utf-8") as f: 98 f.write(html) 99 print(output_file) 100 101 # 将报告发送到邮件 102 Send_email.send_mail_report("UI自动化测试报告!!!") 103 104 if __name__ == '__main__': 105 test = CustomAirtestCase() 106 root = os.path.abspath(".") 107 print("root_path路径: " + root) 108 109 device = ['android://127.0.0.1:5037/TPG4C18308000271'] 110 # device = ['android://127.0.0.1:5037/2476a88e','android://127.0.0.1:5037/99.12.74.40:7237'] 111 112 test.run_air(root_dir=root, device=device)
report.py文件
1 # -*- coding: utf-8 -*- 2 3 import os 4 import io 5 import types 6 import shutil 7 import json 8 import jinja2 9 from airtest.utils.compat import decode_path 10 import airtest.report.report as R 11 12 HTML_FILE = "log.html" 13 HTML_TPL = "log_template.html" 14 STATIC_DIR = os.path.dirname(R.__file__) 15 16 def get_parger(ap): 17 ap.add_argument("script", help="script filepath") 18 ap.add_argument("--outfile", help="output html filepath, default to be log.html") 19 ap.add_argument("--static_root", help="static files root dir") 20 ap.add_argument("--log_root", help="log & screen data root dir, logfile should be log_root/log.txt") 21 ap.add_argument("--record", help="custom screen record file path", nargs="+") 22 ap.add_argument("--export", help="export a portable report dir containing all resources") 23 ap.add_argument("--lang", help="report language", default="en") 24 ap.add_argument("--plugins", help="load reporter plugins", nargs="+") 25 return ap 26 27 28 def get_script_info(script_path): 29 script_name = os.path.basename(script_path) 30 result_json = {"name": script_name, "author": None, "title": script_name, "desc": None} 31 return json.dumps(result_json) 32 33 34 def _make_export_dir(self): 35 dirpath = self.script_root 36 logpath = self.script_root 37 # copy static files 38 for subdir in ["css", "fonts", "image", "js"]: 39 dist = os.path.join(dirpath, "static", subdir) 40 shutil.rmtree(dist, ignore_errors=True) 41 self.copy_tree(os.path.join(STATIC_DIR, subdir), dist) 42 43 return dirpath, logpath 44 45 46 def report(self, template_name, output_file=None, record_list=None): 47 """替换LogToHtml中的report方法""" 48 self._load() 49 steps = self._analyse() 50 # 修改info获取方式 51 info = json.loads(get_script_info(self.script_root)) 52 53 if self.export_dir: 54 self.script_root, self.log_root = self._make_export_dir() 55 output_file = os.path.join(self.script_root, HTML_FILE) 56 self.static_root = "static/" 57 58 if not record_list: 59 record_list = [f for f in os.listdir(self.log_root) if f.endswith(".mp4")] 60 records = [os.path.join(self.log_root, f) for f in record_list] 61 62 if not self.static_root.endswith(os.path.sep): 63 self.static_root = self.static_root.replace("\\", "/") 64 self.static_root += "/" 65 66 data = {} 67 data['steps'] = steps 68 data['name'] = os.path.basename(self.script_root) 69 data['scale'] = self.scale 70 data['test_result'] = self.test_result 71 data['run_end'] = self.run_end 72 data['run_start'] = self.run_start 73 data['static_root'] = self.static_root 74 data['lang'] = self.lang 75 data['records'] = records 76 data['info'] = info 77 78 return self._render(template_name, output_file, **data) 79 80 81 def get_result(self): 82 return self.test_result 83 84 85 def main(args): 86 # script filepath 87 path = decode_path(args.script) 88 record_list = args.record or [] 89 log_root = decode_path(args.log_root) or path 90 static_root = args.static_root or STATIC_DIR 91 static_root = decode_path(static_root) 92 export = decode_path(args.export) if args.export else None 93 lang = args.lang if args.lang in ['zh', 'en'] else 'zh' 94 plugins = args.plugins 95 96 # gen html report 97 rpt = R.LogToHtml(path, log_root, static_root, export_dir=export, lang=lang, plugins=plugins) 98 # override methods 99 rpt._make_export_dir = types.MethodType(_make_export_dir, rpt) 100 rpt.report = types.MethodType(report, rpt) 101 rpt.get_result = types.MethodType(get_result, rpt) 102 103 rpt.report(HTML_TPL, output_file=args.outfile, record_list=record_list) 104 105 return rpt.get_result() 106 107 108 if __name__ == "__main__": 109 import argparse 110 ap = argparse.ArgumentParser() 111 args = get_parger(ap).parse_args() 112 print(str(args) + " 111111111111111") 113 basedir = os.path.dirname(os.path.realpath(__file__)) 114 print(basedir) 115 logdir = os.path.realpath(args.script) 116 print(logdir + "2222222") 117 118 # 聚合结果 119 results = [] 120 121 # 遍历所有日志 122 for subdir in os.listdir(logdir): 123 if os.path.isfile(os.path.join(logdir, subdir)): 124 continue 125 args.script = os.path.join(logdir, subdir) 126 args.outfile = os.path.join(args.script, HTML_FILE) 127 result = {} 128 result["name"] = subdir 129 result["result"] = main(args) 130 results.append(result) 131 132 # 生成聚合报告 133 env = jinja2.Environment( 134 loader=jinja2.FileSystemLoader(basedir), 135 extensions=(), 136 autoescape=True 137 ) 138 template = env.get_template("summary_template.html") 139 html = template.render({"results": results}) 140 141 output_file = os.path.join(logdir, "summary.html") 142 with io.open(output_file, 'w', encoding="utf-8") as f: 143 f.write(html) 144 print(output_file)
summary_template.html文件
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>测试结果汇总</title> 5 <meta charset="UTF-8"> 6 <style> 7 .fail { 8 color: red; 9 width: 7emem; 10 text-align: center; 11 } 12 .success { 13 color: green; 14 width: 7emem; 15 text-align: center; 16 } 17 .details-col-elapsed { 18 width: 7em; 19 text-align: center; 20 } 21 .details-col-msg { 22 width: 7em; 23 text-align: center; 24 background-color:#ccc; 25 } 26 27 </style> 28 </head> 29 <body> 30 <div> 31 <div><h2>Test Statistics</h2></div> 32 33 <table width="800" border="thin" cellspacing="0" cellpadding="0"> 34 <tr width="600"> 35 <th width="300" class='details-col-msg'>案例名称</th> 36 <th class='details-col-msg'>执行结果</th> 37 </tr> 38 {% for r in results %} 39 <tr width="600"> 40 <td class='details-col-elapsed'><a href="{{r.name}}.log/log.html" target="view_window">{{r.name}}</a></td> 41 <td class="{{'success' if r.result else 'fail'}}">{{"成功" if r.result else "失败"}}</td> 42 </tr> 43 {% endfor %} 44 </table> 45 </div> 46 </body> 47 </html>
六、生成的报告
6.1、发送到邮箱的格式
因为我的demo脚本只有一个,所以列表现在只有一个【多个脚本会在列表展示】
6.2、浏览器打开导出文件格式
6.3、每个测试用例的报告
本文来自博客园,作者:爱笑的眼睛真美,转载请注明原文链接:https://www.cnblogs.com/zhangxue521/p/12349789.html