之前已经介绍了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有更好的开源功能

airTest 应用到项目并优化的更多相关文章

  1. 仿百度壁纸客户端(六)——完结篇之Gallery画廊实现壁纸预览已经项目细节优化

    仿百度壁纸客户端(六)--完结篇之Gallery画廊实现壁纸预览已经项目细节优化 百度壁纸系列 仿百度壁纸客户端(一)--主框架搭建,自定义Tab + ViewPager + Fragment 仿百度 ...

  2. 仿百度壁纸client(六)——完结篇之Gallery画廊实现壁纸预览已经项目细节优化

    仿百度壁纸client(六)--完结篇之Gallery画廊实现壁纸预览已经项目细节优化 百度壁纸系列 仿百度壁纸client(一)--主框架搭建,自己定义Tab + ViewPager + Fragm ...

  3. vue+webpack+element-ui项目打包优化速度与app.js、vendor.js打包后文件过大

    从开通博客到现在也没写什么东西,最近几天一直在研究vue+webpack+element-ui项目打包速度优化,想把这几天的成果记录下来,可能对前端牛人来说我这技术比较菜,但还是希望给有需要的朋友提供 ...

  4. 【Vuejs】335-(超全) Vue 项目性能优化实践指南

    点击上方"前端自习课"关注,学习起来~ 前言 Vue 框架通过数据双向绑定和虚拟 DOM 技术,帮我们处理了前端开发中最脏最累的 DOM 操作部分, 我们不再需要去考虑如何操作 D ...

  5. vue大型项目高性能优化----想说爱你真的不容易

    一.背景   目前公司的电子合同采用表单设计器+合同业务配合实现,做了半年多后终于上线,但是下边员工普遍反映卡顿,甚至卡死,爆栈.尤其是新增和修改合同页面,因为这部分数据量大,逻辑复杂,很容易崩溃,所 ...

  6. 简记某WebGIS项目的优化之路

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1. 背景 该项目为研究生时的老师牵头,个人已毕业数年,应老师要求协助其 ...

  7. EF+MVC+cod First项目性能优化总结

    1.EF:this.Configuration.UseDatabaseNullSemantics = true; //关闭数据库null比较行为 2.实体必填字段要加:[Required]属性,可定长 ...

  8. iOS开发--项目内存优化

    在用非ARC模式编写iOS程序的时候,造成程序内存泄露在所难免,后期我们一般会进行内存优化.自己比较常用的内存优化方法有两种 1.Analyze,静态分析内存泄露的方法.很简单,在Xcode菜单栏中点 ...

  9. 小型 Web 页项目打包优化方案

    背景   目前团队中新的 Web 项目基本都采用了 Vue 或 React ,加上 RN,这些都属于比较重量级的框架,然而对于小型 Web 页面,又显得过大.早期的一些项目则使用了较原始的 HTML ...

随机推荐

  1. 洛谷P1040 加分二叉树(树形dp)

    加分二叉树 时间限制: 1 Sec  内存限制: 125 MB提交: 11  解决: 7 题目描述 设一个n个节点的二叉树tree的中序遍历为(l,2,3,...,n),其中数字1,2,3,...,n ...

  2. 剖析ElasticSearch核心概念,NRT,索引,分片,副本等

    ElasticSearch 的核心概念 Near RealTime(NRT) 近实时 近实时有两种意思,一种是从写入数据到可以被搜索到有一个小延迟(大概一秒),还有一种就是基于ElasticSearc ...

  3. consul配置参数大全、详解、总结

    命令行选项 以下选项全部在命令行中指定. -advertise - 通告地址用于更改我们通告给集群中其他节点的地址.默认情况下,-bind地址是通告的.但是,在某些情况下,可能存在无法绑定的可路由地址 ...

  4. memcached—Java操作Memcached实例

    前面博客介绍了如何在Windows操作系统中安装Memcached,总结一下如何使用Java操作Memcached实例: 代码一: package com.ghj.packageoftool; imp ...

  5. Linux常用命令 笔记

     Linux常用命令  笔记 一.文件处理命令 1. ls命令:显示目录文件                          -a 显示所有文件,包括隐藏文件.(all)               ...

  6. C#学习-图解教程(2):访问修饰符(其中两种)

    学习内容:C#:学习书籍:图解教程(中文第四版). 目录:第四章 类的基本概念 -----> 4.8 访问修饰符 访问修饰符 从类的内部,任何函数成员都可以使用成员的名称访问类中任意的其他成员. ...

  7. mysql免解压版安装教程步骤

    首先我这里演示的是mysql-5.6.27-winx64这个免解压的版本 添加环境变量(如添加了则跳过该步骤) 操作如下: )右键单击我的电脑->属性->高级系统设置(高级)->环境 ...

  8. java+Selenium+TestNg搭建自动化测试架构(2)实现跨浏览器功能

    1.切换浏览器类:其中包含了切换浏览器的方法,以及关闭浏览器,设置等待时间,以及重写的断言方法 package com.rrx.framework; import java.io.IOExceptio ...

  9. sublimit 编辑器 设置默认的编码

    1.首选项>>设置 - 用户 2.加上:"default_encoding": "UTF-8"

  10. H5转图片支持保存

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...