Airtest 批量执行用例及生成聚合报告

  前面的文章介绍了使用 bat 执行用例,后面我也说过不会使用这种方式,因为,生成的用例报告都是单个的,不利于整体观察,所以有了今天的要写的内容,如何生成聚合报告。其实,这个原理是很简单的,就是将之前的单用例报告地址添加到一个新开发的 html页面中。最终的效果如下图:有用例数、通过率、设备信息、每个用例在不同的设备上执行情况、查看详情。下面,逐步展开讲解。

请添加图片描述

  在讲之前,先划分好项目目录,这个比较重要,在项目根目录创建三个目录:case(专门用来保存测试用例)、log(保存日志,以及单个测试用例报告)、result(存放单个用例在不同设备的执行状态以及报告地址)。我们要在多机执行用例,那就要获取设备信息,这是第一步要做的事情,Airtest 有个 ADB 模块,使用该模块的方法获取:[ dev[0] for dev in ADB().devices() ],返回一个设备列表,先放这里,后面再用。然后,在获取测试用例,其实就是遍历保存用例的目录,将其加入到列表中。接下来就是执行主程序了,把设备列表、用例列表传递进去,循环获取用例,里面嵌套循环设备,让用例在每个设备上执行,这个点就是拼接 Airtest 用例执行命令,然后通过管道执行 cmd 命令并记录结果。下面通过代码的形式进行讲解,会更加直观。

import traceback
import subprocess
import webbrowser
import time
import json
import shutil
from airtest.core.android.adb import ADB
from jinja2 import Environment, FileSystemLoader

# 主程序入口
def run(devices, cases):
    """
        先清除以前的log、result
    """
    report_log = get_report_dir()
    if os.path.isdir(report_log):
        shutil.rmtree(report_log)
        os.mkdir(report_log)

    log_dir = os.path.join(os.getcwd(), 'log')
    if os.path.isdir(log_dir):
        shutil.rmtree(log_dir)

    try:
        data_r = []
        global time_s
        time_s = time.time()
        for case in cases:
            results = load_json_data(case)
            tasks = run_on_multi_device(devices, case, results)
            for task in tasks:
                status = task['process'].wait()
                results['tests'][task['dev']] = run_one_report(task['case'], task['dev'])  # {'status': -1, 'device': dev, 'path': ''}
                results['tests'][task['dev']]['status'] = status

                name = case.split(".")[0]
                file = os.path.join(report_log, name + "_data.json")
                json.dump(results, open(file, "w"), indent=4)
            data_r.append(results)
        print(data_r)
        run_summary(data_r)
    except Exception as e:
        traceback.print_exc()

# 在多台设备上运行airtest 脚本
def run_on_multi_device(devices, case, results):
    tasks = []
    for dev in devices:
        log_dir = get_log_dir(case,dev)
        print("执行脚本,保存日志路径====>" + str(log_dir))
        # 这里进行了参数化操作,通过 --mobile 获取传入的参数,但是这个参数要添加到 runner_parser  方法中
        if dev == "ce091719b82fc830027e":
            phone = "15511416644"
        elif dev == "AYK4C17C13003612":
            phone = "17703719999"

        # 命令行执行:airtest run xxx.air --device Android://127.0.0.1:5037/b7f0c036 --log xxx/log/xxx.log/xxxxxxxx/
        cmd = [ "airtest", "run", os.path.join(os.getcwd(),"case", case), "--device", "Android:///" + dev, "--log", log_dir, "--mobile",phone]
        try:
            tasks.append({
                'process': subprocess.Popen(cmd, cwd=os.getcwd()),
                'dev': dev,
                'case': case
            })
        except Exception as e:
            traceback.print_exc()
    return tasks

#  生成单个用例测试报告, 就是点击每个用例的详情页面
def run_one_report(case, dev):
    try:
        log_dir = get_log_dir(case,dev)
        log = os.path.join(log_dir, 'log.txt')
        print("生成报告,日志读取日志路径===>>" + str(log_dir))
        if os.path.isfile(log):

            cmd = [ "airtest", "report", os.path.join(os.getcwd(),"case", case), "--log_root", log_dir, "--outfile", os.path.join(log_dir, 'log.html'), "--lang", "zh" ]
            ret = subprocess.call(cmd, shell=True, cwd=os.getcwd())
            return {
                'status': ret,
                # 这里使用的相对路径,目的是便于移植,这里的结构是 log/用例名字/设备/报告.html
                'path': os.path.join(".\\log", case.replace(".air",".log"),dev,'log.html') # os.path.join(os.getcwd(), "log", "follow.log", "log.html")  查看详细报告的相对路径
            }
        else:
            print("Report build Failed. File not found in dir %s" % log)
    except Exception as e:
        traceback.print_exc()
    return {'status': -1, 'device': dev, 'path': ''}

# 加载进度 返回一个空的进度数据
def load_json_data(case):
    return {
        'start': time.time(),
        'script': case,
        'tests': { }
    }

# 生成汇总的测试报告
def run_summary(data):
    try:
        for dt in data:
            res = get_value_by_key(dt, "status")

        summary = {
            'time': "%.3f" % (time.time() - time_s),
            'success': res.count(0),
            'count': len(res)
        }
        summary['start_all'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time_s))
        summary["result"] = data
        print("summary ====>", summary)
        # 聚合报告模板,自己定义开发,也可以借用我的,需要的找我要
        env = Environment(loader=FileSystemLoader(os.getcwd()), trim_blocks=True)
        html = env.get_template('report_template.html').render(data=summary)

        with open("result.html", "w", encoding="utf-8") as f:
            f.write(html)
        # 执行完毕打开聚合报告,可以关闭
        webbrowser.open("result.html")
    except Exception as e:
        traceback.print_exc()

# 获取 key 为 status 的值
def get_value_by_key(in_json, target_key,results=[]):
    for key, value in in_json.items():  # 循环获取key,value
        if key == target_key:
            results.append(value)
        if isinstance(value, dict):
            get_value_by_key(value, target_key)
    return results

# 获取路径下所有air的测试用例文件
def get_cases():
    cases = []
    for name in os.listdir(os.path.join(os.getcwd(),"case")):  # 遍历当前路径下的文件夹和文件名称
        if name.endswith(".air"):
            cases.append(name)
    return cases

# 用例-设备 日志目录
def get_log_dir(case, device=None):
    log_dir = os.path.join(os.getcwd(), 'log', case.replace(".air", ".log"), device.replace(".", "_").replace(':', '_'))
    if not os.path.isdir(log_dir):
        os.makedirs(log_dir)
    return log_dir

# 测试报告目录
def get_report_dir():
    report_path = os.path.join(os.getcwd(), "result")
    if not os.path.isdir(report_path):
        os.mkdir(report_path)
    return report_path

def sort_cases(cases, loginAir, outAir):
    # 清除列表中的登录、退出登录,然后将其分别添加到列表的第一位和最后一位
    cases.remove(loginAir)
    cases.remove(outAir)
    cases.insert(0, loginAir)
    cases.insert(len(cases), outAir)
    return cases

if __name__ == '__main__':
    """
        初始化数据
    """
    # 获取所有已连接的设备列表
    devices = [tmp[0] for tmp in ADB().devices()]
    # 设置指定设备执行测试用例
    # devices = ["BTY4C16705003852","b7f0c036"]
    # 获取所有测试用例
    cases = get_cases()
    # 将登录用例排在最前面执行,退出用例排在最后面执行
    #sort_airs = sort_cases(airs, "loginPro.air", "loginOutPro.air")
    # 获取指定用例,按顺序执行
    # sort_airs = ["openCardPro.air","openOrderPro.air","quickMoneyPro.air"]
    """
        执行脚本
    """
    # 运行所有脚本
    run(devices, cases)

  到这里,基本上就ok了,后面大家可以根据自己需要,将报告移到指定的服务器目录,将报告通过邮件、钉钉等发送出去,点击连接就可以看到全部结果,我这里省略了。里面用到的聚合报告的模板,如果需要可以找我要,也可以让前端开发一个。到此,Airtest 告一段落,后面遇到特殊的情况,再向大家分享。

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值