一、前情概要
但凡接触过性能测试的都对以下情景深有体会:
1.测试前置工作量大繁琐,手工操作费时费力;
2.测试结果有出入,自己想再确认一下,测试过程再来一遍吧;好不容易整出了一份报告,RD一看对结果不满意存在质疑,来回修改几遍整个测试过程就需要再来几遍,整个过程下来精疲力竭;
3.性能数据整理细致入微,测试5分钟,数据整理2小时,一顿操作下来眼睛要瞎;
二、项目背景
为了减少以上痛苦对身体带来的一万点暴击伤害,以及从整个测试模式、方法来看,急需要一套自动化性能测试方案来提高整个性能测试的效率;同时随着DevOps(DevOps 强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件)在各个一二线大厂的推行,性能测试任务接入流水线用于周期性触发执行也是大势所趋,因此一套全自动化的性能测试方案亟待推出用于解决工作的难点问题,提高整个的工作效率。
三、解决的主要问题
1.测试过程繁琐,人工介入成本高;
2.测试结果有出入,自己想再确认一下,测试过程再来一遍吧;好不容易整出了一份报告,RD一看对结果不满意存在质疑,来回修改几遍整个测试过程就需要再来几遍,整个过程下来精疲力竭;
3.性能数据整理细致入微,测试5分钟,数据整理2小时,一顿操作下来眼睛要瞎;
四、实现方案
整体框架:
Python + Appium + Emmagee
实现原理:
Python脚本实现Appium对Emmagee的驱动-》进而调起测试APP中的测试场景和case-》运行-》性能测试数据自动读取-》数据统计、分析-》输出测试报告
实现场景:
目前已实现支持基础地图、个性化地图、热力图、海量Polyline地图场景下放大、缩小、拖拽等多种操作下的性能数据的全自动输出
Demo示例:
第一步:材料准备
1.待测APP/待测场景
2.Emmage性能测试工具
3.Appium工具
4.Python 3.7环境
第二步:按步骤来实现每个阶段的自动化
明确要实现自动化的操作步骤,如上面实现原理所描述的"当准备好待测APP/待测场景后,实现操作-》运行-》数据收集-》统计分析-》输出报告"的全自动化,因此按步骤来实现每个阶段的自动化。
1.Python脚本实现Appium对Emmagee的驱动
# performance_test_tool.py
import os
import shutil
import time
from appium import webdriver
from appium.webdriver.common.touch_action import TouchAction
from auto_analysis_tool import analysis_output_test_report
"""
入参说明:
open_dir_path:Emmagee 输出的原始性能数据文件所在目录(文件按修改时间降序排列)
output_file_name:最终输出的性能数据报告文件名名称和所在路径
start_line:截取原始性能数据文件开始行
end_line:截取原始性能数据文件结束行
"""
def start_testing(open_dir_path, output_file_name, start_line, end_line):
# appium服务监听地址
server = 'http://localhost:4723/wd/hub'
# app启动参数
desired_caps = {
# "platformName": "Android",
# "platformVersion": "6.0",
# "deviceName": "T7G0215A31011599",
# "appPackage": "com.netease.qa.emmagee",
# "appActivity": ".activity.MainPageActivity",
# # "appWaitActivity": ".activity.MainPageActivity",
# # "appWaitPackage": "com.netease.qa.emmagee",
# "appiumVersion": "2.5.1"
"platformName": "Android",
"platformVersion": "5.1.1",
"deviceName": "T3Q4C16A17003811",
"appPackage": "com.netease.qa.emmagee",
"appActivity": ".activity.MainPageActivity"
}
desired_caps['unicodeKeyboard'] = True
desired_caps['resetKeyboard'] = True
# 驱动
driver = webdriver.Remote(server, desired_caps)
# 点击设置按钮
driver.find_element_by_id("com.netease.qa.emmagee:id/btn_set").click()
time.sleep(2)
# 设置采集频率为1s
TouchAction(driver).tap(x=97, y=449).perform()
# 点击返回按钮
driver.find_element_by_id("com.netease.qa.emmagee:id/go_back").click()
time.sleep(5)
# 获取测试APP RadioButton
radio_button = driver.find_element_by_xpath(
"/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.ListView/android.widget.LinearLayout[5]/android.widget.RadioButton")
radio_button = driver.find_element_by_xpath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.ListView/android.widget.LinearLayout[8]/android.widget.RadioButton")
# 选中测试APP RadioButton
radio_button.click()
# 点击开始测试按钮
driver.find_element_by_id("com.netease.qa.emmagee:id/test").click()
# 运行测试场景,如1分钟
time.sleep(60)
# 点击停止测试按钮
TouchAction(driver).tap(x=453, y=286).perform()
# kill测试Demo
os.system("adb shell am force-stop baidumapsdk.demo")
time.sleep(5)
# 清除目标目录下的文件
del_file("./Emmagee/")
# adb pull 生成的测试报告到目标文件夹下
os.system("adb pull /sdcard/Emmagee/ ./")
# 解析测试报告内容:内存、CPU信息,并输出分析后的测试报告
analysis_output_test_report(open_dir_path, output_file_name, start_line, end_line)
# 关闭会话
driver.quit()
"""清空文件夹内容"""
def del_file(filepath):
"""
删除某一目录下的所有文件或文件夹
:param filepath: 路径
:return:
"""
if os.path.isdir(filepath):
del_list = os.listdir(filepath)
for f in del_list:
file_path = os.path.join(filepath, f)
if os.path.isfile(file_path):
os.remove(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
2.性能数据收集、解析、输出报告
'''
# auto_analysis_tool.py
自动化工具:自动解析原始性能报告并输出分析后的测试报告
'''
import os
import csv
import numpy
"""
对外暴露接口:解析测试报告内容:内存、CPU信息,并输出分析后的测试报告
"""
def analysis_output_test_report(open_dir_path, output_file_name, start_line, end_line):
# 获取原始测试报告文件路径
origin_file_path = get_report_path(open_dir_path)
# 打开原始测试报告文件
file = open(origin_file_path, encoding="gbk")
# 读取内容并解析cpu/memory信息
data = csv.reader(file)
cpu = []
memory = []
for row in data:
if data.line_num < start_line:
continue
# 只获取指定count_number行
if data.line_num > end_line:
break
cpu.append(float(row[5])) # 将字符串数据转化为浮点型加入到数组之中
memory.append(float(row[2]))
print(data.line_num, row)
cpu_mean = numpy.mean(cpu) # 输出cpu均值
cpu_max = max(cpu) # 输出cpu最大值
memory_mean = numpy.mean(memory) # 输出memory均值
memory_max = max(memory) # 输出memory最大值
print(cpu_mean)
print(cpu_max)
print(memory_mean)
print(memory_max)
# 以约定格式输出性能数据
write_analysis_report(output_file_name, cpu_mean, cpu_max, memory_mean, memory_max)
file.close()
"""
获取测试报告文件路径
"""
def get_report_path(open_file_path):
dir_list = os.listdir(open_file_path) # 列出文件夹下所有的目录与文件
if not dir_list:
return
else:
# 注意,这里使用lambda表达式,将文件按照最后修改时间顺序升序排列
# os.path.getmtime() 函数是获取文件最后修改时间
# os.path.getctime() 函数是获取文件最后创建时间
dir_list = sorted(dir_list, key=lambda x: os.path.getmtime(os.path.join(open_file_path, x)), reverse=True)
test_report_path = os.path.join(open_file_path, dir_list[0]) # 获取Emmagee最新生成的测试报告文件
print(test_report_path)
return test_report_path
'''输出分析后的测试报告'''
def write_analysis_report(output_file_name, cpu_mean, cpu_max, memory_mean, memory_max):
with open(output_file_name, 'w', encoding="gbk") as file_object:
file_object.write(
"cpu_mean:" + str(cpu_mean) + "\t" + "cpu_max:" + str(cpu_max) + "\t" + "memory_mean:" + str(
memory_mean) + "\t" + "memory_max:" + str(memory_max))
3.单个测试case运行
# test_case1.py
from performance_test_tool import start_testing
start_testing("/Users/username/Documents/workSpace/PerformAutoTesting/Emmagee/",
"/Users/username/Documents/workSpace/PerformAutoTesting/outputReport/case1.txt", 11,
12)
4.性能报告文件输出
# case1.txt
cpu_mean:17.495 cpu_max:27.59 memory_mean:89.375 memory_max:98.31
5.附件(Emmagee原始性能数据文件示例.csv)