2、python+monkey 自动化稳定性测试

一、实现原理

使用python模拟终端输入monkey命令,同时抓取ERROR级别及以上的logcat日志,最后通过数据处理判定是否测试通过(结果含有"Monkey finished"结果为pass、含有
“System appears to have crashed"结果为false)并筛选monkey日志中的crash和anr数量

二、环境

python + monkey

三、项目目录结构

 
四、配置文件

yaml文件存储测试数据:  

data:
  phone : Y2J5T17524006969           
  monkeyclickcount : 10            # Monkey点击次数
  packages : 要测试的包名              # 要测试的包名
  path_text : 'logs/text.log'         # 运行日志保存地址
  error: 'logs/error.log'             # 错误日志保存地址
  throttle : 500                      # 事件的时延,单位是毫秒
  send: 1000 100                      # 用于指定伪随机数生成器的seed值
  syskeys: 70 1000                    # 调整“系统”按键事件的百分比(这些按键通常被保留,由系统使用,如Home、Back、Start Call、End Call及音量控制键)
  appswitch: 10 1000                  # 调整其它类型事件的百分比。它包罗了所有其它类型的事件,如:按键、其它不常用的设备按钮、等等
  count : 3                           # 暂时用不上,如果后期需要执行次数的话可以使用
  touch : 80                          # 设置操作事件的百分比  显示详细信息,随机执行80个事件,


五、data文件读取yaml文件内容:

import yaml,os
 
# path1 = os.path.abspath('../config/config.yaml')
# print   (path1)
 
class OpenYaml:
 
    def __init__(self,file_name=None):
        if file_name:
            self.file_name = file_name
        else:
            self.file_name = './config/config.yaml'
        self.data = self.getdata()
 
    def getdata(self):
        # 读取yaml的值
        with open(self.file_name,'r',encoding='utf-8') as f:
            self.data = yaml.load(f,Loader=yaml.FullLoader)
            return self.data
 
if __name__ == '__main__':
 
    tt  = OpenYaml()
    print(tt.getdata()['data']['phone'])


六、logs:分别存储执行流日志和错误日志:

七、monkeyss 执行Monkey命令:

from data.raad_yaml import OpenYaml
import os
import gevent
 
def get_devices():
    command_result = ("adb devices")  # 执行adb命令用于判断设备是否连接正常
    mydevice = os.popen(command_result)  # 执行adb 命令
    mystr = mydevice.read()  # 获取命令后的内容
    splits = mystr[25:41]  # 获取设备号
    mal = OpenYaml()  # 读取yaml 文件
 
    if splits  in mal.getdata()['data']['phone']:
        """
        phone:设备号 如果更换可在 yaml 文件中更改
        判断设备是否连接成功,如果未连接或者连接成功设备号不正确不执行~
        """
        print('设备连接正常,开始执行Monkey命令~')
        MonkeyCmd = "adb shell monkey -p %s --pct-touch %s -v -v -v --ignore-crashes --ignore-timeouts %s - s %s --throttle %s  1>%s 2>%s" \
                    % (mal.getdata()['data']['packages'],  # 测试的包名
                       mal.getdata()['data']['touch'],     # 显示详细信息,随机执行80个事件
                       mal.getdata()['data']['monkeyclickcount'],  # 点击次数
                       mal.getdata()['data']['send'],  # 用于指定伪随机数生成器的seed值
                       mal.getdata()['data']['throttle'],  # 事件的时延,单位是毫秒
                       mal.getdata()['data']['path_text'],  # 运行日志保存路径
                       mal.getdata()['data']['error'])  # 错误日志保存路径
        os.popen(MonkeyCmd)
        """ 如果不执行某些事件,再命令行中注释掉,注意删除对应的 %s 值 """
        print('执行命令:', MonkeyCmd)
 
    else:
        print('设备链接失败,请检查设备连接后再试~/或设备号是否正确:', splits)
 
get_devices()

方法二:可以获取多个设备,指定多个设备运行

python三种方式自动获取多个安卓adb设备名
 

方法1:
使用os.popen方式

import os
 
def check_adb_devices():
    '''
    检查adb 设备,并返回设备sn list
    
    :return: 设备sn list
    '''
    adb_list=[]
    ret =os.popen('adb devices').readlines()
    print('ret={}'.format(ret))
    if len(ret) ==1:
        print('未识别到adb 设备...')
        return adb_list
    else:
        for n in ret:
            if '\tdevice\n' in n:
                adb=str(n).strip().split('\tdevice')[0].strip()
                adb_list.append(str(adb))
 
        print('adb设备数量={},adb_list={}'.format(len(adb_list), adb_list))
        return adb_list
 
 
if __name__ == '__main__':
    check_adb_devices()
执行结果:
ret=['List of devices attached\n', '1234a4f3\tdevice\n', 'mn4xwsbfrd\tdevice\n', '\n']
adb设备数量=2,adb_list=['1234a4f3', 'mn4xwsbfrd']


方法2:
使用正则表达式方式匹配

def check_adb_devices_re():
    '''
    用re正则方式获取adb 列表,并返回设备sn list
    
    :return: 设备sn list
    '''
    # 获取所有的安卓设备SN号码
    devices_list = []
    str_list = os.popen('adb devices').readlines()
    print(str_list)
    count = 0
    for i in str_list:
        if '\tdevice' in i:
            device_name = ''
            device_name = re.sub('\tdevice', '', i).replace('\n', '').strip()
            print("Device_{}_name={}".format(count, device_name))
            devices_list.append(device_name)
            count = count + 1
    print("devices={},devices_list={}".format(count,devices_list))
    return devices_list
执行结果:
['List of devices attached\n', '1234a4f3\tdevice\n', '\n']
Device_0_name=1234a4f3
devices=1,devices_list=['1234a4f3']


方法3:
实际使用中发现,方法1和方法2在直接运行python文件的时候没有问题,但是把py文件打包成exe程序后,就会出现无法获取到cmd命令的返回值,经过分析为:打包后程序的cmd调试窗口被禁用了,就会导致程序无法获取返回值。

解决方法
将adb devices的返回结果写入到txt文件中,再逐行读取,并进行字符串匹配,检测实际的adb设备名;

代码实现
运行cmd指令函数

def run_cmd( cmd_str='', echo_print=1):
    """
    执行cmd命令,不显示执行过程中弹出的黑框
    备注:subprocess.run()函数会将本来打印到cmd上的内容打印到python执行界面上,所以避免了出现cmd弹出框的问题
    :param cmd_str: 执行的cmd命令
    :return: 
    """
    from subprocess import run
    if echo_print == 1:
        print('\n执行cmd指令="{}"'.format(cmd_str))
    run(cmd_str, shell=True)


获取多个adb设备名的函数

def check_adb_devices_ini():
    global devices_list
    import os
    import re
    
    # 获取所有的安卓设备SN号码
    devices_list = []
    adb_devices_file = '.\\Config\\adb.ini'
    if os.path.exists(adb_devices_file):
        run_cmd('del /f /q ' + adb_devices_file)
    print(run_cmd('adb devices >' + adb_devices_file))
    
    f = open(adb_devices_file, "r")
    str_list = f.readlines()
    print(str_list)
    if 'device' not in str(str_list[1]):
        print('adb devices:{}。未识别到任何adb设备,请确认安卓设备已正确连接且USB调试已打开...'.format(str_list))
        print("未识别到任何设备,请确认设备已正确连接上...")
        return -1
    
    count = 1
    for i in str_list:
        if '\tdevice' in i:
            device_name = ''
            device_name = re.sub('\tdevice', '', i).replace('\n', '').strip()
            print("Device_{}_name={}".format(count, device_name))
            devices_list.append(device_name)
            count = count + 1
    print("devices_list={}".format(devices_list))
    return devices_list


调用方法

 

if __name__ == '__main__':
    check_adb_devices_ini()
执行结果
执行cmd指令="del /f /q .\Config\adb.ini"
 
执行cmd指令="adb devices >.\Config\adb.ini"
None
['List of devices attached\n', '3123caad\tdevice\n', 'mn4xwsb3ed\tdevice\n', '\n']
Device_1_name=3123caad
Device_2_name=mn4xwsb3ed
devices_list=['3123caad', 'mn4xwsb3ed']

代码(根据自身需求更改第60行的monkey命令)

# coding:utf-8
 
import os, time
import sys, re
import random
import subprocess
 
device = ""
package = ""
number = 0
result_true = []
result_false = []
crash_flag = 0
anr_flag = 0
 
 
# 获取设备名称
def devices():
    global device, flag
    cmd1 = "adb devices > content/devices.csv"
    d = os.system(cmd1)
    with open("content/devices.csv", encoding="utf-8", mode="r") as f:  # 筛选出进程包名和activity
        lines = f.readlines()
        for line in lines:
            if "device" in line:
                device = line.split('    ')[0]
 
 
# 获取包名
def get_package():
    global package
    cmd = 'adb shell dumpsys window | grep mCurrentFocus > content/info.csv'  # info.csv文件中是当前activity概括信息
    d = os.system(cmd)
    with open("content/info.csv", encoding="utf-8", mode="r") as f:  # 筛选出进程包名和activity
        lines = f.readlines()
        for line in lines:
            if "mCurrentFocus" in line:
                if "null" in line:
                    continue
                value1 = line.split('{')[1]
                if 'mode' in value1:
                    value2 = value1.split(' ')[4]
                else:
                    value2 = value1.split(' ')[2]
                if "\n" in value2:
                    value = value2.strip("}\n")
                    package = value.split('/')[0]
                else:
                    value = value2.strip("}")
                    package = value.split('/')[0]
                # print(value)
            else:  # 未获取到包名信息则停止脚本
                print("未获取到package信息!脚本终止执行!")
 
 
# 开始执行monkey并获取log
def start_monkey():
    global device, package, number
    number = random.randint(1, 500)
    cmd1 = f"adb -s {device} shell monkey -p {package} -v -v -v --throttle 10 -s {number} --ignore-crashes --ignore-timeouts --ignore-native-crashes --pct-syskeys 0 --pct-anyevent 0 800000 > log/monkeylog.txt"
    cmd2 = f"adb -s {device} logcat -v time *:E > log/logcat.txt"
    # print(f"包名:{package}")
    subprocess.Popen(cmd2, shell=True)
    time.sleep(1)
    subprocess.Popen(cmd1, shell=True)
    time.sleep(1)
 

 
# 判断monkey是否已经执行结束
def is_finish():
    global result_true, result_false
    with open('log/monkeylog.txt', encoding='utf-8', mode='r') as f:
        lines = f.read()
        pattern1 = re.compile(f'Monkey finished', re.IGNORECASE)
        pattern2 = re.compile(f'System appears to have crashed', re.IGNORECASE)
        result_true = pattern1.findall(lines)
        result_false = pattern2.findall(lines)
        # print(f"result_ture:{result_true}\nresult_false:{result_false}")
 
# 筛选出monkey日志中crash和anr的数量
def select():
    global crash_flag,anr_flag
    with open('log/monkeylog.txt', encoding='utf-8', mode='r') as f:
        lines = f.readlines()
        for line in lines:
            if "crash" in line.lower():
                crash_flag += 1
            if "anr" in line:
                anr_flag += 1
 
 
if __name__ == '__main__':
    devices()
    get_package()
    start_monkey()
    while True:
        is_finish()
        if "Monkey finished" in result_true:   #结果含有"Monkey finished"结果为pass
            print(f"本次稳定性测试种子值为:{number}")
            select()
            print(f"monkey日志中存在{crash_flag}处crash")
            print(f"monkey日志中存在{anr_flag}处anr")
            # cmd = "adb shell && exit"
            # os.system(cmd)
            exit("pass")
        elif "System appears to have crashed" in result_false:  #System appears to have crashed"结果为false
            print(f"本次稳定性测试种子值为:{number}")
            exit("false")
        else:
            time.sleep(1)

八、创建bugreport.txt报告,并生成html文件

执行完成monkey,生成txt日志后,

创建bugreport.txt报告,并生成html文件

bugreport参考文档:bugreport获取及chkbugreport工具分析_幸福的达哥的博客-CSDN博客

完成

九、start.bat 文件再Dos中运行,或者结合jenkins

python monkeyss.py
 

十、杀死后台测试程序APP

def killAPP(config):
    """
    杀死后台测试程序APP。
    :return:Null
    """
    killApp = "adb -s %s shell am force-stop %s" % (config.get("phone"), config.get("packageName"))
    os.popen(killApp)
    print("******已清理后台测试程序APP。")

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值