三、airtest批量执行用例

我们编写的用例不可能只有一个,执行起来也不可能是单个执行,需要批量执行用例,所有本片文档针对如何批量执行用例作说明。

1、项目结构确定,一般项目尽量按照固定结构来设计,有利于批量执行用例和项目用例的维护。项目的结构需要按照如下图所示的结构来设计,下图项目是我以前做的项目结构。

 

 批量执行用例在airtestIDE上是无法实现的,IDE只能单用例执行,所以这里通过脚本来实现。完整代码如下所示:

# -*- coding: utf-8 -*-

import unittest
import os
import sys
import six
import traceback
import types
import time
from io import open
from airtest.cli.parser import runner_parser
from airtest.core.api import G, auto_setup, log
from airtest.core.settings import Settings as ST
from airtest.utils.compat import decode_path
from copy import copy

class MyAirtestCase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.args = args

        setup_by_args(args)

        # setup script exec scope
        cls.scope = copy(globals())

    def setUp(self):
        if self.args.log and self.args.recording:
            for dev in G.DEVICE_LIST:
                try:
                    dev.start_recording()
                except:
                    traceback.print_exc()
        # 设置日志路径
        auto_setup(logdir=self._logdir)

    def tearDown(self):
        if self.args.log and self.args.recording:
            for k, dev in enumerate(G.DEVICE_LIST):
                try:
                    output = os.path.join(self.args.log, "recording_%d.mp4" % k)
                    dev.stop_recording(output)
                except:
                    traceback.print_exc()

    def runTest(self):
        try:
            # 调用脚本中的runCase方法并传递scope供脚本使用
            self.runCase(self.scope)
        except Exception as err:
            tb = traceback.format_exc()
            log("Final Error", tb)
            six.reraise(*sys.exc_info())

    @property
    def logdir(self):
        return self._logdir

    @logdir.setter
    def logdir(self, value):
        self._logdir = value

def setup_by_args(args):
    # init devices
    if isinstance(args.device, list):
        devices = args.device
    elif args.device:
        devices = [args.device]
    else:
        devices = []
        print("do not connect device")

    # set base dir to find tpl
    args.script = decode_path(args.script)

    # set log dir
    if args.log is True:
        print("save log in %s/log" % args.script)
        args.log = os.path.join(args.script, "log")
    elif args.log:
        print("save log in '%s'" % args.log)
        args.log = decode_path(args.log)
    else:
        print("do not save log")

    # guess project_root to be basedir of current .air path
    project_root = os.path.dirname(args.script) if not ST.PROJECT_ROOT else None
    # 此处不设置日志路径,防止生成多余的log.txt
    auto_setup(args.script, devices, None, project_root)

def new_case(py, logdir):
    """实例化MyAirtestCase并绑定runCase方法"""
    with open(py, 'r', encoding="utf8") as f:
        code = f.read()
    obj = compile(code.encode("utf-8"), py, "exec")
    ns = {}
    ns["__file__"] = py
    # exec obj in ns
    exec(obj, ns)
    func = ns["runCase"]
    case = MyAirtestCase()
    pyfilename = os.path.basename(py).replace(".py", "")
    # 设置属性以便在setUp中设置日志路径
    case.logdir = os.path.join(logdir, pyfilename)
    # 绑定runCase方法
    case.runCase = types.MethodType(func, case)
    return case

def init_log_folder():
    """初始化日志根目录"""
    name = time.strftime("log_%Y%m%d_%H%M%S", time.localtime())
    if not os.path.exists(name):
        os.mkdir(name)
    return name

def run_script(parsed_args, testcase_cls=MyAirtestCase):
    global args  # make it global deliberately to be used in MyAirtestCase & test scripts
    args = parsed_args
    dir = os.path.dirname(os.path.realpath(__file__))
    suites = []
    pys = []

    # 获取所有用例集
    for f in os.listdir(dir):
        if f.endswith("用例集"):
            f = os.path.join(dir, f)
            if os.path.isdir(f):
                suites.append(f)

    # 获取所有脚本
    for s in suites:
        for f in os.listdir(s):
            if f.endswith(".py") and not f.startswith("__"):
                pys.append(os.path.join(s, f))

    logdir = os.path.join(dir, init_log_folder())
    args.log = logdir
    suite = unittest.TestSuite()

    # 添加脚本
    for py in pys:
        case = new_case(py, logdir)
        suite.addTest(case)

    result = unittest.TextTestRunner(verbosity=0).run(suite)
    if not result.wasSuccessful():
        sys.exit(-1)

if __name__ == "__main__":
    ap = runner_parser()
    args = ap.parse_args()
    run_script(args, MyAirtestCase)

下面对逻辑进行说明:

2、继承TestCase实现测试用例类,run_script方法的测试类是MyAirtestCase,而MyAirtestCase继承unittest的TestCase类。其中setUpClass和setUp方法是用例执行前进行设备、参数的设置;runTest真正开始执行用例,tearDown方法在用例执行完毕后,输出log。

3、实例化MyAirtestCase,将项目下所有的py代码用例绑定到MyAirtestCase的runCase方法中,最后返回测试用例。

 4、遍历读取用例并将用例加入测试套件中,在run_script方法里面找到所有的脚本,然后将脚本添加到unittest的测试套件里面,最后通过执行测试套件来实现。下面是是核心的执行方法。附件中是全部runner代码。

 5、最后,通过main函数入库开始执行用例即可,其中runner_parser是airtest.cli.parser下运行时添加到log中的参数。实现的具体方法是runner_parser。

 runner_parser实现log参数数据设置。在run_scrpit中会有调用到。代码如下所示。

通过上面几步,就可以实现免IDE实现批量运行了,当然,如果想要多机器批量运行的话,此脚本还不能实现,需要另外实现多线程调度实现,这里先不做介绍,后续继续接受设置report报告。

猜你喜欢

转载自blog.csdn.net/x_xingduo_2315/article/details/123646035