Airtest一:批量运行脚本、汇总报告、导出报告

 
 

一、项目目录

二、各文件说明

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
 

四、 说明

我的demo是基于图像识别的浏览器case,没有连接设备,执行手机的在my_runner.py将devices添加进去即可
 

五、各文件源码

 

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_runner.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 
 17 
 18 class CustomAirtestCase(AirtestCase):
 19     # @classmethod
 20     # def setUpClass(cls):
 21     #     super(CustomAirtestCase,cls).setUpClass()
 22 
 23 
 24     def setUp(self):
 25         print("custom setup")
 26         super(CustomAirtestCase, self).setUp()
 27 
 28     def tearDown(self):
 29         print("custom tearDown")
 30         super(CustomAirtestCase, self).setUp()
 31 
 32     def run_air(self, root_dir='', device=[], scriptname='air_case'):
 33         # 用例目录
 34         script_path = root_dir + "/" + scriptname
 35         # 聚合结果
 36         results = []
 37         # 创建log文件
 38         root_log = root_dir + '/' + 'log'
 39         if os.path.isdir(root_log):
 40             shutil.rmtree(root_log)
 41         else:
 42             os.makedirs(root_log)
 43             print(str(root_log) + '  is created')
 44 
 45         # 创建export_log文件
 46         export_log = root_dir + '/' + 'export_log'
 47         if os.path.isdir(export_log):
 48             shutil.rmtree(export_log)
 49         else:
 50             os.makedirs(export_log)
 51             print(str(export_log) + '  is created')
 52 
 53         for f in os.listdir(script_path):
 54             if f.endswith(".air"):
 55                 # f为.air案例名称:login.air
 56                 airName = f
 57                 script = os.path.join(script_path, f)
 58                 # airName_path为.air的全路径/Users/zhangxue/Documents/study/airtest_fppui/air_case/login.air
 59                 print("当前运行脚本路径:" + str(script))
 60                 # 日志存放路径和名称:/Users/zhangxue/Documents/study/airtest_fppui/log/login/log.html
 61                 log = os.path.join(root_dir, 'log' + '/' + airName.replace('.air', ''))
 62                 print("log路径:" + str(log))
 63                 if os.path.isdir(log):
 64                     shutil.rmtree(log)
 65                 else:
 66                     os.makedirs(log)
 67                     print(str(log) + '  is created')
 68                 # global args
 69                 args = Namespace(device=device, log=log, recording=None, script=script, compress=1)
 70                 try:
 71                     run_script(args, AirtestCase)
 72                 except:
 73                     pass
 74                 finally:
 75                     export_output_file = os.path.join(export_log + "/" + airName.replace('.air', '.log') + '/log.html')
 76                     rpt = report.LogToHtml(script_root=script, log_root=log, export_dir=export_log)
 77                     rpt.report("log_template.html", output_file=export_output_file)
 78                     result = {}
 79                     result["name"] = airName.replace('.air', '')
 80                     result["result"] = rpt.test_result
 81                     results.append(result)
 82 
 83         # 生成聚合报告
 84         env = jinja2.Environment(
 85             loader=jinja2.FileSystemLoader(root_dir),
 86             extensions=(),
 87             autoescape=True
 88         )
 89         template = env.get_template("summary_template.html", root_dir)
 90         html = template.render({"results": results})
 91         output_file = os.path.join(export_log, "summary.html")
 92         with io.open(output_file, 'w', encoding="utf-8") as f:
 93             f.write(html)
 94         print(output_file)
 95 
 96 
 97 if __name__ == '__main__':
 98     test = CustomAirtestCase()
 99     root = os.path.abspath(".")
100     print("root_path路径:  " + root)
101 
102     device = ['']
103 
104     test.run_air(root)
105 
106 my_runner.py文件
my_runner.py文件

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)
report.py文件

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           7emem;
10          text-align: center;
11         }
12         .success {
13             color: green;
14           7emem;
15          text-align: center;
16         }
17       .details-col-elapsed {
18           7em;
19          text-align: center;
20       }
21       .details-col-msg {
22           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>
summary_template.html文件

原文地址:https://www.cnblogs.com/zhangxue521/p/12349789.html