zoukankan      html  css  js  c++  java
  • 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有更好的开源功能

  • 相关阅读:
    746. 使用最小花费爬楼梯
    1283. 使结果不超过阈值的最小除数
    307. 区域和检索
    303. 区域和检索
    474. 一和零
    600. 不含连续1的非负整数
    命名规范【转】
    .NET中RabbitMq的使用
    C#中json字符串的序列化和反序列化
    c#发送post请求(带参数)
  • 原文地址:https://www.cnblogs.com/Ronaldo-HD/p/10784273.html
Copyright © 2011-2022 走看看