近期开发反应:Monkey/自动遍历测试时出现的FC、ANR等问题,通过log无法准确定位问题原因和复现路径,希望测试时能增加录屏,便于定位问题复现路径及原因。(自动遍历测试请参考:https://github.com/bytedance/Fastbot_Android)
手机自带录屏缺陷:
Monkey/自动遍历测试时,通过adb命令间断录屏,然后解析测试log,若未出现报错信息,就删除之前的录屏,反之,保留并导出录屏。
流程图.jpg
4.1、Python环境和源码所需的第三方库
4.2、根据需求修改monkey_through.yaml配置信息(注意字段间的空格)
4.3、开启设备自带的log,保持usb连接设备,测试中请勿中断
4.4、执行main()方法(出现异常会自动终止测试,并导出录屏至脚本目录)
# 测试类型:Monkey/Through
testType: Through
# 【Monkey】
Monkey:
# 包名
packageName: com.google.android.dialer
# 运行次数
runTimes: 500
# 种子值
runSeed: 999
# 事件流间隔(单位:毫秒)
runThrottle: 500
pctTouch: 50
pctMotion: 20
pctTrackball: 5
pctSyskeys: 5
pctAppSwitch: 5
pctRotation: 5
# 【自动遍历】
Through:
# 包名
packageName: com.example.anrfcapp
# 测试时钟(单位:分钟)
runMinutes: 10
# 时间流间隔
runThrottle: 500
# 录屏配置
screenRecord:
# 录制时长(单位:秒)
recordTime: 60
#!/usr/bin/python
# -*- coding: utf-8 -*-
# @Time : 2021/11/15 16:31
# @Author : CuiShuangqi
# @Email : [email protected]
# @File : monkeyScreenRecord.py
"""
需求:整合Monkey、自动遍历测试,并增加录屏功能(间断录屏,无报错则删除之前的录屏)
注:测试前请修改monkey_through.yaml配置文件
"""
import os
import time
import yaml
# 获取手机信息
def get_device_info():
# 等待手机连接
print("等待设备连接...")
os.system("adb wait-for-device")
print("设备连接成功,参数如下:")
# 手机型号
phoneModel = os.popen("adb shell getprop ro.product.model").read().strip('\n')
# 手机SOC
socModel = os.popen("adb shell getprop ro.board.platform").read().strip('\n').upper()
# 软件版本
softwareVersion = os.popen("adb shell getprop ro.build.display.id").read().strip('\n')
# 安卓版本
androidVersion = os.popen("adb shell getprop ro.build.version.release").read().strip('\n')
print(f"【手机型号】:{phoneModel}",
f"\n【手机平台】:{socModel}",
f"\n【软件版本】:{softwareVersion}",
f"\n【安卓版本】:{androidVersion}")
# 读取配置信息
def read_yaml(test_type: str):
yamlPath = os.path.join(os.getcwd(), "monkey_through.yaml")
with open(yamlPath, "r", encoding="GBK") as f:
return yaml.load(f.read(), Loader=yaml.FullLoader)[test_type]
# 判断手机测试环境
def is_test_environment(test_type: str) -> bool:
# 脚本当前所在目录
curPath = os.getcwd()
flag = False
# Monkey测试
if test_type == "Monkey":
print("Monkey测试无需配置测试环境!")
flag = True
# 自动遍历测试
elif test_type == "Through":
jarFiles = os.popen("adb shell find /sdcard/ -name '*.jar'").read()
if "/sdcard/framework.jar" in jarFiles and "/sdcard/monkeyq.jar" in jarFiles:
print("设备内已存在framework.jar、monkeyq.jar!")
flag = True
else:
cur_files = []
for root, dirs, files in os.walk(os.getcwd()):
for f in files:
cur_files.append(os.path.join(root, f))
frameworkJarPath = os.path.join(curPath, "framework.jar")
monkeyqJarPath = os.path.join(curPath, "monkeyq.jar")
# 判断脚本目录是否有jar包
if frameworkJarPath in cur_files and monkeyqJarPath in cur_files:
# 当前目录存在jar包,推送jar包至/sdcard/
if os.system(f"adb push {frameworkJarPath} /sdcard/") == 0 and \
os.system(f"adb push {monkeyqJarPath} /sdcard/") == 0:
print("推送framework.jar、monkeyq.jar至/sdcard/目录成功!")
flag = True
else:
print("推送framework.jar、monkeyq.jar至/sdcard/目录失败!")
flag = False
else:
print("脚本当前目录不存在framework.jar、monkeyq.jar!")
flag = False
else:
print("测试类型参数错误!")
flag = False
return flag
def test_monkey_or_through(test_type: str):
# 后台运行
if test_type == "Monkey":
monkeyCommand = f'adb shell "monkey ' \
f'-p {read_yaml("Monkey")["packageName"]} ' \
f'--throttle {read_yaml("Monkey")["runThrottle"]} ' \
f'--randomize-throttle ' \
f'--ignore-crashes ' \
f'--ignore-timeouts ' \
f'--ignore-security-exceptions ' \
f'--ignore-native-crashes ' \
f'--pct-touch {read_yaml("Monkey")["pctTouch"]} ' \
f'--pct-motion {read_yaml("Monkey")["pctMotion"]} ' \
f'--pct-trackball {read_yaml("Monkey")["pctTrackball"]} ' \
f'--pct-syskeys {read_yaml("Monkey")["pctSyskeys"]} ' \
f'--pct-appswitch {read_yaml("Monkey")["pctAppSwitch"]} ' \
f'--pct-rotation {read_yaml("Monkey")["pctRotation"]} ' \
f'-v -v -v {read_yaml("Monkey")["runTimes"]} ' \
f'1>/sdcard/monkeyInfo.txt 2>/sdcard/monkeyError.txt &"'
os.system(monkeyCommand)
elif test_type == "Through":
throughCommand = f'adb shell ' \
f'"CLASSPATH=/sdcard/monkeyq.jar:/sdcard/framework.jar ' \
f'exec app_process /system/bin com.android.commands.monkey.Monkey ' \
f'-p {read_yaml("Through")["packageName"]} ' \
f'--agent robot ' \
f'--running-minutes {read_yaml("Through")["runMinutes"]} ' \
f'--throttle {read_yaml("Through")["runThrottle"]} -v -v -v ' \
f'1>/sdcard/throughInfo.txt 2>/sdcard/throughError.txt &"'
os.system(throughCommand)
# 屏幕录制
def screen_record(test_type: str) -> str:
# 录制秒数
recordTime = read_yaml("screenRecord")["recordTime"]
# 录制文件名
timeFlag = time.strftime('%Y-%m-%d_%H%M%S', time.localtime(time.time()))
recordName = f"/sdcard/{test_type}_{timeFlag}.mp4"
print("正在录制屏幕...请勿断开连接!")
recordCommand = f'adb shell screenrecord --time-limit {recordTime} {recordName}'
os.system(recordCommand)
print("录制屏幕已结束!")
# 返回录制mp4的文件名,便于无异常时删除该文件
return recordName
# 判断手机是否存在异常
def is_Exception(testType: str) -> bool:
flag = False
if testType == "Monkey":
# 解析/sdcard/monkeyError.txt
throughErrorInfo = os.popen('adb shell cat /sdcard/monkeyError.txt').read()
if "ANR" in throughErrorInfo or "NOT RESPONDING" in throughErrorInfo or "CRASH" in throughErrorInfo:
flag = True
print("Monkey测试存在异常!!!")
elif testType == "Through":
# 解析/sdcard/throughError.txt
throughErrorInfo = os.popen('adb shell cat /sdcard/throughError.txt').read()
if "ANR" in throughErrorInfo or "NOT RESPONDING" in throughErrorInfo or "CRASH" in throughErrorInfo:
flag = True
print("自动遍历存在异常!!!")
# 导出视频
else:
print("此次测试未发现异常!")
return flag
# main
def main():
get_device_info()
testType = read_yaml("testType")
if is_test_environment(testType):
# 开始测试
test_monkey_or_through(testType)
while True:
# 录屏
recordName = screen_record(testType)
findMonkeyPsCommand = 'adb shell ps | findstr -i "monkey"'
commandList = os.popen(findMonkeyPsCommand).read().strip("\n").split(" ")
if commandList[0] != "":
# 判断是否发生异常
if is_Exception(testType):
# 导出录屏至当前目录
if os.system(f"adb pull {recordName} {os.getcwd()}") == 0:
print(f"视频已导出至:【{os.getcwd()}】")
else:
print(f"导出视频失败,已存放:{recordName}")
# 查询monkey进程id结果,并去空
myList = [i for i in commandList if i != '']
# 取列表的第二个值,即为monkey进程id
monkeyId = myList[1]
# kill 掉遍历进程
if os.system(f"adb shell kill {monkeyId}") == 0:
print("进程结束成功!")
else:
print("进程结束失败,请尝试手动结束进程!")
break
else:
# 删除之前的录屏,并重新录屏
if os.system(f"adb shell rm {recordName}") == 0:
print("删除录屏成功!")
else:
print("删除录屏失败!")
break
else:
# 测试已结束
print("测试已结束,未发现异常,已保存测试最后一次录屏!")
break
else:
print("初始化测试环境失败!")
if __name__ == '__main__':
main()
下面是配套资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!
最后: 可以在公众号:程序员小濠 ! 免费领取一份216页软件测试工程师面试宝典文档资料。以及相对应的视频学习教程免费分享!,其中包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。
如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!喜欢软件测试的小伙伴们,可以加入我们的测试技术交流扣扣群:779450660里面有各种软件测试资源和技术讨论)