上期回顾:Airtest生成报告命令行airtest report详解
以下基于
python3.8;airtestIDE1.2.11;airtest1.2.2;pocoui1.0.83
上期我们讲了在命令行生成报告,这次我们看下怎么通过脚本直接在代码中运行生成报告。分别是LogToHtml类和simple_report()函数。
LogToHtml
上期我们讲airtest report时,源码里已经看到了,最终生成报告,就是实例化LogToHtml类,并调用了里面的report()方法,那这次我们详细看下其源码。
先看下LogToHtml类的初始化
1# 文件位置:your_python_path/site-packages/airtest/report/report.py
2class LogToHtml(object):
3 """Convert log to html display """
4 def __init__(self, script_root, log_root="", static_root="", export_dir=None, script_name="", logfile=None, lang="en", plugins=None):
5 self.log = []
6 self.script_root = script_root
7 self.script_name = script_name
8 if not self.script_name or os.path.isfile(self.script_root):
9 self.script_root, self.script_name = script_dir_name(self.script_root)
10 self.log_root = log_root or ST.LOG_DIR or os.path.join(".", DEFAULT_LOG_DIR)
11 self.static_root = static_root or STATIC_DIR
12 self.test_result = True
13 self.run_start = None
14 self.run_end = None
15 self.export_dir = export_dir
16 self.logfile = logfile or getattr(ST, "LOG_FILE", DEFAULT_LOG_FILE)
17 self.lang = lang
18 self.init_plugin_modules(plugins)
参数说明:
-
script_root:脚本所在文件夹
-
log_root:log.txt所在文件夹
-
static_root:部署静态资源的服务器路径
-
export_dir:导出报告文件夹
-
script_name:脚本名称
-
logfile:log.txt的路径
-
lang:报告的语言,中文:zh;英文:en(默认)
-
plugins:插件,使用了poco框架,需要填写
--plugin poco.utils.airtest.report
。如果用到了airtest-selenium,需要填写`--plugin airtest_selenium.report
源码解析:
只说下重要的
第8行:如果没有指定脚本名称,则使用script_dir_name()通过前面给的脚本所在文件夹自行获取
第10行:指定了log.txt所在文件夹则直接赋值,否则使用全局变量ST.LOG_DIR给的路径(就是说你可以在代码最开始通过全局变量指定日志路径)。如果都没有,则取当前目录下的log文件夹。
第16行:如果指定了log.txt全路径则直接赋值,否则使用全局变量ST.LOG_FILE给的路径(默认是log.txt,你可以在代码最开始通过全局变量指定日志全路径)
LogToHtml类里有各种方法去获取生成报告所需的各种数据,这里我们只看一个我们直接接触到的:
1 def _translate_desc(self, step, code):
2 """ 函数描述 """
3 if step['tag'] != "function":
4 return None
5 name = step['data']['name']
6 res = step['data'].get('ret')
7 args = {i["key"]: i["value"] for i in code["args"]}
8
9 desc = {
10 "snapshot": lambda: u"Screenshot description: %s" % args.get("msg"),
11 "touch": lambda: u"Touch %s" % ("target image" if isinstance(args['v'], dict) else "coordinates %s" % args['v']),
12 "swipe": u"Swipe on screen",
13 "wait": u"Wait for target image to appear",
14 "exists": lambda: u"Image %s exists" % ("" if res else "not"),
15 "text": lambda: u"Input text:%s" % args.get('text'),
16 "keyevent": lambda: u"Click [%s] button" % args.get('keyname'),
17 "sleep": lambda: u"Wait for %s seconds" % args.get('secs'),
18 "assert_exists": u"Assert target image exists",
19 "assert_not_exists": u"Assert target image does not exists",
20 }
21
22 # todo: 最好用js里的多语言实现
23 desc_zh = {
24 "snapshot": lambda: u"截图描述: %s" % args.get("msg"),
25 "touch": lambda: u"点击 %s" % (u"目标图片" if isinstance(args['v'], dict) else u"屏幕坐标 %s" % args['v']),
26 "swipe": u"滑动操作",
27 "wait": u"等待目标图片出现",
28 "exists": lambda: u"图片%s存在" % ("" if res else u"不"),
29 "text": lambda: u"输入文字:%s" % args.get('text'),
30 "keyevent": lambda: u"点击[%s]按键" % args.get('keyname'),
31 "sleep": lambda: u"等待%s秒" % args.get('secs'),
32 "assert_exists": u"断言目标图片存在",
33 "assert_not_exists": u"断言目标图片不存在",
34 }
35
36 if self.lang == "zh":
37 desc = desc_zh
38
39 ret = desc.get(name)
40 if callable(ret):
41 ret = ret()
42 return ret
看到了吗,我们看的报告中的步骤描述,就是在这里定义的,如果你想修改,可以直接修改这里。
实例化LogToHtml类之后,就可以使用类中的方法report()生成报告了
1 def report(self, template_name=HTML_TPL, output_file=HTML_FILE, record_list=None):
2 """
3 Generate the report page, you can add custom data and overload it if needed
4
5 :param template_name: default is HTML_TPL
6 :param output_file: The file name or full path of the output file, default HTML_FILE
7 :param record_list: List of screen recording files
8 :return:
9 """
10 if not self.script_name:
11 path, self.script_name = script_dir_name(self.script_root)
12
13 if self.export_dir:
14 self.script_root, self.log_root = self._make_export_dir()
15 # output_file可传入文件名,或绝对路径
16 output_file = output_file if output_file and os.path.isabs(output_file) \
17 else os.path.join(self.script_root, output_file or HTML_FILE)
18 if not self.static_root.startswith("http"):
19 self.static_root = "static/"
20
21 if not record_list:
22 record_list = [f for f in os.listdir(self.log_root) if f.endswith(".mp4")]
23 data = self.report_data(output_file=output_file, record_list=record_list)
24 return self._render(template_name, output_file, **data)
参数说明:
-
template_name:就是/your_python_path/site-packages/airtest/report/log_template.html,感兴趣可以打开看看以及看下源码。另外,你也可以传入自己的模板。
-
output_file:日志全路径,默认为log.html
-
record_list:录像文件列表
源码解析:
前面分别获取脚本路径和名称、导出文件和路径、录像列表。
之后调用report_data()方法,该方法就是获取所有报告信息的,里面调用了LogToHtml类中的很多方法去获取生成报告所需的各种各样的数据,感兴趣的可以自己看看。
最后调用并返回_render(),该方法其实就是用jinja2配合HTML模板生成报告(Jinja2是Python下的一个模板引擎,用来生成HTML网页)
可以看下_render()的实现:
1 @staticmethod
2 def _render(template_name, output_file=None, **template_vars):
3 """ 用jinja2渲染html"""
4 env = jinja2.Environment(
5 loader=jinja2.FileSystemLoader(STATIC_DIR),
6 extensions=(),
7 autoescape=True
8 )
9 env.filters['nl2br'] = nl2br
10 env.filters['datetime'] = timefmt
11 template = env.get_template(template_name)
12 html = template.render(**template_vars)
13
14 if output_file:
15 with io.open(output_file, 'w', encoding="utf-8") as f:
16 f.write(html)
17 LOGGING.info(output_file)
18
19 return html
实例演示
1__author__ = '公众号:测试工程师小站'
2
3from airtest.core.api import *
4from airtest.report.report import LogToHtml
5
6auto_setup(__file__,logdir=True)
7
8touch([500, 500]) # 此一句代表整个脚本
9
10r = LogToHtml(script_root=r'D:\code\Airtest\test1.air', log_root=r'D:\code\Airtest\test1.air\log', export_dir=r'D:\code\Airtest\report\test1', lang='zh')
11r.report() # 用法1:生成报告就在导出文件夹下,名字为log.html
12
13# 用法2:所有的资源文件在导出文件夹,但HTML文件在report文件夹,且叫custom_report.html
14# 个人不建议html文件与资源文件分离,放在一起不好管理吗?
15# r.report(output_file=r'D:\code\Airtest\report\custom_report.html')
将用例代码和生成报告代码写一起,有个缺点就是用例失败后,后面的代码就不会执行了,可以加try,改造后的代码
1__author__ = '公众号:测试工程师小站'
2
3from airtest.core.api import *
4from airtest.report.report import LogToHtml
5
6auto_setup(__file__,logdir=True)
7try:
8 touch([500, 500]) # 此一句代表整个脚本
9except:
10 print('发生异常,在这写异常处理语句,或是重新运行一遍脚本')
11finally: # 不管脚本成功与否,都会执行finally块中的语句
12 r = LogToHtml(script_root=r'D:\code\Airtest\test1.air', log_root=r'D:\code\Airtest\test1.air\log', export_dir=r'D:\code\Airtest\report\test1', lang='zh')
13 r.report()
现在还有个问题,就是用代码生成报告,所有东西我们都写死了,这样每次报告都会相互覆盖,如果想保留每次报告,我们只需要将报告导出路径自定义一下,比如每次都导到一个新的文件夹,名字为:用例名_日期。
1__author__ = '公众号:测试工程师小站'
2
3import time, os
4from airtest.core.api import *
5from airtest.report.report import LogToHtml
6
7auto_setup(__file__,logdir=True)
8try:
9 touch([500, 500]) # 此一句代表整个脚本
10except:
11 print('发生异常,在这写异常处理语句,或是重新运行一遍脚本')
12finally: # 不管脚本成功与否,都会执行finally块中的语句
13 casename = os.path.basename(__file__) # 用例名就取文件名,或者你也可以通过其他方式定义
14 casename = casename.split('.')[0]
15 dt = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
16 dirname = casename + dt
17 r = LogToHtml(script_root=r'D:\code\Airtest\test1.air', log_root=r'D:\code\Airtest\test1.air\log', export_dir=dirname, lang='zh')
18 r.report()
simple_report()
simple_report()与LogToHtml类在同一个文件中,是一个独立的函数。
1# 文件位置:your_python_path/site-packages/airtest/report/report.py
2def simple_report(filepath, logpath=True, logfile=None, output=HTML_FILE):
3 path, name = script_dir_name(filepath)
4 if logpath is True:
5 logpath = os.path.join(path, getattr(ST, "LOG_DIR", DEFAULT_LOG_DIR))
6 rpt = LogToHtml(path, logpath, logfile=logfile or getattr(ST, "LOG_FILE", DEFAULT_LOG_FILE), script_name=name)
7 rpt.report(HTML_TPL, output_file=output)
参数说明:
-
filepath:对应的是LogToHtml初始化时的script_root参数,脚本文件的全路径,可以直接传入变量__file__
-
logpath:对应的是LogToHtml初始化时的log_root参数。布尔值,为True时,使用全局变量ST.LOG_DIR给的路径(就是说你可以在代码最开始通过全局变量指定日志路径),如果都没有,则取当前目录下的log文件夹
-
logfile:对应的是LogToHtml初始化时的logfile参数,log.txt的文件路径
-
output:对应的是report()方法中的output_file参数,报告导出全路径,必须以.html结尾
源码解析:
第1行获取脚本路径;
第2、3行获取日志路径;
第5行实例化LogToHtml;
第6行调用LogToHtml类的report()方法生成报告。
代码逻辑很简单,实际上调用的还是LogToHtml。但是使用simple_report()无法导出报告,只能在本地查看。
实例演示
我个人觉得simple_report()码如其名,就是给大家一个最简单的生成报告的方法。所以我个人觉得使用时就应该全用默认值参数:
1__author__ = '公众号:测试工程师小站'
2
3from airtest.core.api import *
4from airtest.report.report import simple_report
5
6auto_setup(__file__,logdir=True)
7
8touch([500, 500]) # 此一句代表整个脚本
9 # 其他参数全用默认的,即在当前脚本路径下生成名为log.html的报告
10simple_report(__file__)
以上就是LogToHtml类和simple_report()函数的使用了。但如果你有意将自动化做的正规且容入项目流程中,那必然要涉及CI,比如用Jenkins自动执行。如果要自动执行,那就还是得用命令行调用的方法(airtest run、airtest report)。
所以本篇内容不建议你实用,仅做为了解生成报告的内在逻辑学习,实用的还是看看前2期的airtest run、airtest report。
---------------------------------------------------------------------------------
关注微信公众号即可在手机上查阅,并可接收更多测试分享~