Airtest生成报告命令行airtest report详解
上期回顾:Airtest命令行运行airtest run详解
以下基于
python3.8;airtestIDE1.2.11;airtest1.2.2;pocoui1.0.83
上期我们讲了在命令行运行脚本,这次接着讲运行完后通过命令行生成报告。
想要用命令运行,必须要装独立于AirtestIDE的Python环境,还没装的可以看下之前的文章Airtest之python本地环境安装、独立IDE运行
装好环境之后,在命令行中输入airtest reprot -h
,如果出现下图,就表示安装成功了
如果你是Windows且提示没有airtest report这个命令,那就是你python的环境变量没配好,网上搜一下配好环境变量。实在不行,试试python -m airtest report -h
先给出一个完整示例大家有个印象:
airtest report "/szh/qasite/qasite.py" --log_root "/szh/qasite/log" --lang zh
--plugin poco.utils.airtest.report --export "/szh/report"
参数说明:
-
脚本路径:airtest report命令后紧跟的第1个参数是脚本的绝对路径,当然你也可以用相对路径,但为了避免路径层级搞乱,我还是推荐大家用绝对路径,简单明了。路径用双引号包住,防止因为特殊符号报错
-
--log_root:表示脚本运行后日志和截图生成的路径,对应airtest run命令中--log填写的路径
-
--outfile:报告生成路径及文件名。注意使用此参数生成的报告,只能在本地查看,无法发送给其他人,强行发送他人,别人打开报告无法查看到图片的(该命令生成的HTML报告源码中,图片的地址写的都是绝对路径,不是相对路径),如
airtest report "/szh/qasite/qasite.py" --log_root "/szh/qasite/log"
--outfile "/szh/qasite/log/my_report.html"
-
--export:导出一个包含所有资源报告的路径,此参数生成的报告可以打包发给其他人正常查看。使用此参数后最好就不要再用--outfile参数了,否则文件会乱,再者都完全导出所有资源了,最好所有文件都在一个文件夹。所以在你没彻底搞懂--export和--outfile的区别和关联前,就只用--export。
-
--lang:此参数默认值是en,表示生成英文报告,zh表示生成中文报告。我们都是中国人,所以记住一定加参数--lang zh就对了。
-
--plugin:插件参数,如果只用的Airtest框架,不用填。如果用到了poco框架,需要填写
--plugin poco.utils.airtest.report
。如果用到了airtest-selenium,需要填写--plugin airtest_selenium.report
。 -
--static_root:还是回到--outfile,使用此参数只生成报告,不额外产生其他资源文件,所以发给其他人时报告会显示异常;使用--export导出的报告会复制一份资源文件,所以可以发给其他人,代价就是多复制了一份资源文件。但每次导出的报告,其静态资源文件夹static中的css、js等文件,每次是不变的,生成的报告足够多的时候,占用的硬盘空间也会越来越多。所以我们可以把静态资源统一放到服务器上(服务器地址必须以http开头),即使用--static_root参数后不再复制静态资源,而是去你指定的服务器上读取。所以使用该参数的命令可以这样写:
airtest report "/szh/qasite/qasite.py" --log_root "/szh/qasite/log" --lang zh --plugin poco.utils.airtest.report
--export "/szh/report --static_root https://host:port/static/css/
生成的报告这里不再展示,感兴趣的可以看之前的文章Airtest报告详解
源码解析
我们看看运行命令之后,代码到底是如何流转的
# 文件位置:your_python_path/site-packages/airtest/__main__.py
# -*- coding: utf-8 -*-
from airtest.cli.__main__ import main
if __name__ == '__main__':
main()
命令行运行airtest后,首先进入到这里,可以看是调用了airtest.cli.__main__
中的 main()方法,进入查看:
# 文件位置:your_python_path/site-packages/airtest/cli/__main__.py
# -*- coding: utf-8 -*-
from airtest.cli.parser import get_parser
def main(argv=None):
ap = get_parser()
args = ap.parse_args(argv)
if args.action == "info":
from airtest.cli.info import get_script_info
print(get_script_info(args.script))
elif args.action == "report":
from airtest.report.report import main as report_main
report_main(args)
elif args.action == "run":
from airtest.cli.runner import run_script
run_script(args)
elif args.action == "version":
from airtest.utils.version import show_version
show_version()
else:
ap.print_help()
if __name__ == '__main__':
main()
(1)首先ap = get_parser()
获取参数
(2)其次args = ap.parse_args(argv)
对参数进行正确性检查
(3)最后根据具体的命令执行不同逻辑,我们可以看到一共有4个子命令:airtest info
获取脚本信息(目前没发现这个功能有啥实际用处,具体可以查看官方文档);airtest report
生成报告,执行了report_main(args);airtest run
运行脚本,执行了run_script(args);airtest version
打印版本号;
如果都没匹配上,则输入帮助命令
下面分别看下(1)(2)(3)的源码:
(1)ap = get_parser()
# 文件位置:your_python_path/site-packages/airtest/cli/parser.py
# -*- coding: utf-8 -*-
import argparse
import sys
from airtest.report.report import get_parger as report_parser
from airtest.cli.runner import setup_by_args
def get_parser():
ap = argparse.ArgumentParser()
subparsers = ap.add_subparsers(dest="action", help="version/run/info/report")
# subparser version
subparsers.add_parser("version", help="show version and exit")
# subparser run
ap_run = subparsers.add_parser("run", help="run script")
runner_parser(ap_run)
# subparser info
ap_info = subparsers.add_parser("info", help="get & print author/title/desc info of script")
ap_info.add_argument("script", help="script filename")
# subparser report
ap_report = subparsers.add_parser("report", help="generate report of script")
report_parser(ap_report)
return ap
def runner_parser(ap=None):
if not ap:
ap = argparse.ArgumentParser()
ap.add_argument("script", help="air path")
ap.add_argument("--device", help="connect dev by uri string, e.g. Android:///", nargs="?", action="append")
ap.add_argument("--log", help="set log dir, default to be script dir", nargs="?", const=True)
ap.add_argument("--compress", required=False, type=int, choices=range(1, 100), help="set snapshot quality, 1-99", default=10)
ap.add_argument("--recording", help="record screen when running", nargs="?", const=True)
ap.add_argument("--no-image", help="Do not save screenshots", nargs="?", const=True)
return ap
可以看到Airtest也是用了argparse这个命令行的库,在'run'时,执行了runner_parser(ap_run)
runner_parser()则是airtest run
命令的具体参数设定
(2)args = ap.parse_args(argv)
# 文件位置:your_python_path/site-packages/airtest/Lib/argparse.py
# =====================================
# Command line argument parsing methods
# =====================================
def parse_args(self, args=None, namespace=None):
args, argv = self.parse_known_args(args, namespace)
if argv:
msg = _('unrecognized arguments: %s')
self.error(msg % ' '.join(argv))
return args
参数检查,如果你把参数输错了或者输入了一个不存在的参数,则会提示'unrecognized arguments',你可以自己随便加一个参数运行试试看,比如airtest report --gongzhonghao qasite
(3)report_main(args)
# 文件位置:your_python_path/site-packages/airtest/report/report.py
def main(args):
# script filepath
path, name = script_dir_name(args.script)
record_list = args.record or []
log_root = decode_path(args.log_root) or decode_path(os.path.join(path, DEFAULT_LOG_DIR))
static_root = args.static_root or STATIC_DIR
static_root = decode_path(static_root)
export = decode_path(args.export) if args.export else None
lang = args.lang if args.lang in ['zh', 'en'] else 'en'
plugins = args.plugins
# gen html report
rpt = LogToHtml(path, log_root, static_root, export_dir=export, script_name=name, lang=lang, plugins=plugins)
rpt.report(HTML_TPL, output_file=args.outfile, record_list=record_list)
main()函数依次获取脚本路径、录像列表(如果airtest run时有录像)、日志路径、静态资源路径、导出报告路径、报告语言、插件参数
之后定义了一个LogToHtml类实例,并调用report()方法,我们继续看下report()
# 文件位置:your_python_path/site-packages/airtest/report/report.py
def report(self, template_name=HTML_TPL, output_file=HTML_FILE, record_list=None):
"""
Generate the report page, you can add custom data and overload it if needed
:param template_name: default is HTML_TPL
:param output_file: The file name or full path of the output file, default HTML_FILE
:param record_list: List of screen recording files
:return:
"""
if not self.script_name:
path, self.script_name = script_dir_name(self.script_root)
if self.export_dir:
self.script_root, self.log_root = self._make_export_dir()
# output_file可传入文件名,或绝对路径
output_file = output_file if output_file and os.path.isabs(output_file) \
else os.path.join(self.script_root, output_file or HTML_FILE)
if not self.static_root.startswith("http"):
self.static_root = "static/"
if not record_list:
record_list = [f for f in os.listdir(self.log_root) if f.endswith(".mp4")]
data = self.report_data(output_file=output_file, record_list=record_list)
return self._render(template_name, output_file, **data)
前面分别获取脚本路径和名称、导出文件和路径、录像列表。
之后调用report_data()方法,该方法就是获取所有报告信息的,里面调用了LogToHtml类中的很多方法去获取生成报告所需的各种各样的数据,感兴趣的可以自己看看。
最后调用并返回_render(),该方法其实就是用jinja2配合HTML模板生成报告(Jinja2是Python下的一个模板引擎,用来生成HTML网页)
以上就是命令大体的一个运行过程。
---------------------------------------------------------------------------------
关注微信公众号即可在手机上查阅,并可接收更多测试分享~