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网页)

以上就是命令大体的一个运行过程。

---------------------------------------------------------------------------------

关注微信公众号即可在手机上查阅,并可接收更多测试分享~