airTest 应用到项目并优化

之前已经介绍了airTest的原理,该文主要指引大家能够将airTest框架应用到具体的测试项目当中去。

首先要考虑的是:

1. 你是用airTest 去做什么自动化 (android, ios, web)

2. airTest 能做什么,不能做什么,然后我们需要做出什么优化?

通过实际的使用,我其实发现airTest最大的优点是在元素识别方面,能够让没有编码基础或者是编码能力比较弱的人也可以编写自动化测试脚本。

但是大家使用的时候也会发现airTest没有良好的用例设计、管理机制。 没有很好的参数管理,同时一个air文件会生成一个测试报告,没有报告聚合的功能。

特别适用于:  通过外包执行功能测试的情况。 外包只要帮你录入脚本就行了。

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

我们需要优化的几个地方:

1. 提供报告聚合功能,一次可以看多个用例的执行情况

2. Log聚合功能

3. 良好的用例设计/参数管理/方法封装的功能。

4. 批量执行脚本的功能

批量执行脚本的代码: (loggin.conf文件可以自行配置,这里不展开。)

import time

from airtest.cli.runner import AirtestCase, run_script
from argparse import *
import shutil
import os
import logging.config

logging.config.fileConfig('../logging.conf')
logger = logging.getLogger('root')
# alltestnames = allcase_list_new.case_list()
# alltestnames = ['webDemo.air', 'webDemo2.air']
# logger.info(alltestnames)
conf_root_dir = 'C:\Python项目\AirtestIDE_2019-01-15_py3_win64\demo\AirtestCase-master\'


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)
        print("creat file_dir: ", name)
    return namedef del_file(file_path):
    for name in os.listdir(file_path):
        if name.startswith("log_20"):
            try:
                shutil.rmtree(name)
            except Exception as e:
                print('filepath: ', file_path)
                print("del failed !!", e)


def copy_file(olddir_path, newdir_path):
    # 遍历路径内的文件,只是一层
    for name in os.listdir(olddir_path):
        if name.endswith(".txt"):  # 只复制特定类型文件
            # print (os.path.join(root, name))
            source = os.path.join(olddir_path, name)
            target = os.path.join(newdir_path, name)
            try:
                shutil.copy(source, target)
                # name.close()
            except:
                print("Copy %s failed!" % name)



class CustomAirtestCase(AirtestCase):
    def setUp(self):
        logger.info("custom setup")
        super(CustomAirtestCase, self).setUp()

    def tearDown(self):
        logger.info("custom tearDown")
        super(CustomAirtestCase, self).setUp()

    def run_air(self, root_dir, device):
        for f in os.listdir(root_dir):
            if f.endswith(".air"):
                # f为.air案例名称:银行.air
                script = os.path.join(root_dir, f)
                logger.info('执行脚本 :' + script)
                # 日志存放路径和名称
                # log = os.path.join(root_dir, + airName)
                # logger.info('用例log保存文件夹=' + log)
                logdir = os.path.join(conf_root_dir, init_log_folder())
                if os.path.isdir(logdir):
                    shutil.rmtree(logdir)
                else:
                    logger.info('日志路径: ' + logdir)
                
                # output_file = log + '\' + 'log.html'
                args = Namespace(device=device, log=logdir, recording=None, script=script)
                try:
                    run_script(args, AirtestCase)
                    # 将log和截图文件等复制到脚本文件下,方便生成报告
                    copy_file(logdir, script)
                except Exception as e:
                    logger.exception(str(e))
                    pass


if __name__ == '__main__':
    test = CustomAirtestCase()
    # device = ['android:2d87aa41']
    # device = ['android:127.0.0.1:62001']
    device = ["windows:///"]
    # for d in device:
    test.run_air(conf_root_dir + '用例集', device)

聚合报告的代码:

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

import os
import io
import types
import shutil
import json
import jinja2
from airtest.utils.compat import decode_path
import airtest.report.report as R

HTML_FILE = "log.html"
HTML_TPL = "log_template.html"
STATIC_DIR = os.path.dirname(R.__file__)

def get_parger(ap):
    ap.add_argument("script", help="script filepath")
    ap.add_argument("--outfile", help="output html filepath, default to be log.html")
    ap.add_argument("--static_root", help="static files root dir")
    ap.add_argument("--log_root", help="log & screen data root dir, logfile should be log_root/log.txt")
    ap.add_argument("--record", help="custom screen record file path", nargs="+")
    ap.add_argument("--export", help="export a portable report dir containing all resources")
    ap.add_argument("--lang", help="report language", default="en")
    ap.add_argument("--plugins", help="load reporter plugins", nargs="+")
    return ap


def get_script_info(script_path):
    script_name = os.path.basename(script_path)
    result_json = {"name": script_name, "author": None, "title": script_name, "desc": None}
    return json.dumps(result_json)


def _make_export_dir(self):
    dirpath = self.script_root
    logpath = self.script_root
    # copy static files
    for subdir in ["css", "fonts", "image", "js"]:
        dist = os.path.join(dirpath, "static", subdir)
        shutil.rmtree(dist, ignore_errors=True)
        self.copy_tree(os.path.join(STATIC_DIR, subdir), dist)

    return dirpath, logpath


def report(self, template_name, output_file=None, record_list=None):
    """替换LogToHtml中的report方法"""
    self._load()
    steps = self._analyse()
    # 修改info获取方式
    info = json.loads(get_script_info(self.script_root))

    if self.export_dir:
        self.script_root, self.log_root = self._make_export_dir()
        output_file = os.path.join(self.script_root, HTML_FILE)
        self.static_root = "static/"

    if not record_list:
        record_list = [f for f in os.listdir(self.log_root) if f.endswith(".mp4")]
    records = [os.path.join(self.log_root, f) for f in record_list]

    if not self.static_root.endswith(os.path.sep):
        self.static_root = self.static_root.replace("\", "/")
        self.static_root += "/"

    data = {}
    data['steps'] = steps
    data['name'] = os.path.basename(self.script_root)
    data['scale'] = self.scale
    data['test_result'] = self.test_result
    data['run_end'] = self.run_end
    data['run_start'] = self.run_start
    data['static_root'] = self.static_root
    data['lang'] = self.lang
    data['records'] = records
    data['info'] = info

    return self._render(template_name, output_file, **data)


def get_result(self):
    return self.test_result


def main(args):
    # script filepath
    path = decode_path(args.script)
    record_list = args.record or []
    log_root = decode_path(args.log_root) or path
    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 'zh'
    plugins = args.plugins

    # gen html report
    rpt = R.LogToHtml(path, log_root, static_root, export_dir=export, lang=lang, plugins=plugins)
    # override methods
    rpt._make_export_dir = types.MethodType(_make_export_dir, rpt)
    rpt.report = types.MethodType(report, rpt)
    rpt.get_result = types.MethodType(get_result, rpt)

    rpt.report(HTML_TPL, output_file=args.outfile, record_list=record_list)

    return rpt.get_result()


if __name__ == "__main__":
    import argparse
    ap = argparse.ArgumentParser()
    args = get_parger(ap).parse_args()
    basedir = os.path.dirname(os.path.realpath(__file__))
    logdir = os.path.realpath(args.script)

    # 聚合结果
    results = []

    # 遍历所有日志
    for subdir in os.listdir(logdir):
        if os.path.isfile(os.path.join(logdir, subdir)):
            continue
        args.script = os.path.join(logdir, subdir)
        args.outfile = os.path.join(args.script, HTML_FILE)
        result = {}
        result["name"] = subdir
        result["result"] = main(args)
        results.append(result)

    # 生成聚合报告
    env = jinja2.Environment(
        loader=jinja2.FileSystemLoader(basedir),
        extensions=(),
        autoescape=True
    )
    print("path: ",basedir)
    template = env.get_template("summary_template.html",basedir)
    html = template.render({"results": results})

    output_file = os.path.join(logdir, "summary.html")
    with io.open(output_file, 'w', encoding="utf-8") as f:
        f.write(html)
    print(output_file)

最终实现过程:

1. 外包测试人员通过airTest的IDE录制脚本用例文件.air, 放入到测试服务器指定的  用例集  目录下

2. 执行测试,生成聚合测试报告

3. 分析,并不断优化。(最好是结合jekins做一个持续集成的闭环)

4. 期待airTest有更好的开源功能

原文地址:https://www.cnblogs.com/Ronaldo-HD/p/10784273.html