Monkey/自动遍历测试,整合屏幕录制需求

1、需求

近期开发反应:Monkey/自动遍历测试时出现的FC、ANR等问题,通过log无法准确定位问题原因和复现路径,希望测试时能增加录屏,便于定位问题复现路径及原因。(自动遍历测试请参考:https://github.com/bytedance/Fastbot_Android)
手机自带录屏缺陷:

  1. 测试时无法控制时长
  2. 测试中容易被中断
  3. 长时间录屏消耗手机内存储过大

2、解决思路

Monkey/自动遍历测试时,通过adb命令间断录屏,然后解析测试log,若未出现报错信息,就删除之前的录屏,反之,保留并导出录屏。

3、流程图

Monkey/自动遍历测试,整合屏幕录制需求_第1张图片

流程图.jpg

4、测试流程

4.1、Python环境和源码所需的第三方库

4.2、根据需求修改monkey_through.yaml配置信息(注意字段间的空格)

4.3、开启设备自带的log,保持usb连接设备,测试中请勿中断

4.4、执行main()方法(出现异常会自动终止测试,并导出录屏至脚本目录)

5、Yaml配置文件示例

# 测试类型: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

5、源代码

#!/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里面有各种软件测试资源和技术讨论)

你可能感兴趣的:(软件测试,技术分享,java,软件测试,测试工程师,测试类型,程序人生)