项目测试过程中经常需要在手机端体验语音产品的识别效果和稳定性,识别效果与手机硬件强相关无法抛开硬件影响。因此开发了一套基于uiautomator2+python UI自动化工具,可以实现在电脑端控制手机demo开始收音+播放待测音频+保存识别结果的APP自动化效果测试工具。
uiautomator2是一个自动化测试开源工具,仅支持Android平台的原生应用测试。它本来是Google提供的一个自动化测试的Java库,后来发展了python-uiautomator2,封装了谷歌自带的uiautomator测试框架,提供便利的python接口,用它可以很便捷的编写python脚本来实现app的自动化测试。uiautomator2 提供了点击、长按、输入文本、滑动、拖拽、截屏等方法,能够模拟用户的各种动作。用户可以通过控件的 id 或 text 等属性,定位到控件,从而对控件实施上述操作。
2.1.1 安装adb
安装adb并使手机与电脑连接(具体安装自行百度)。
adb devices 查看连接设备。
2.1.2 安装uiautomator2
pip install --pre -U uiautomator2
2.1.3 设备安装atx-agent
python -m uiautomator2 init (安装包含httprpc服务的apk到手机)
2.1.4 安装weditor
基于浏览器技术的weditor UI查看器,方便抓取手机上应用的控件。
pip install --pre weditor(安装)
python -m weditor(运行)
2.2.1 设备连接
import uiautomator2 as u2
d = u2.connect('192.168.1.169') #通过WIFI
d = u2.connect_usb('123456f') #通过USB(手机的序列号可以通过adb devices获取到,假设序列号是123456f)
2.2.2 获取当前包名
cmd界面输入“uiautomator2 current ”,能获取手机当前界面APP的包名、activity以及pid等相关信息,如下:{"package": "com.android.browser", "activity": "com.uc.browser.InnerUCMobile", "pid": 28478 }。
2.2.3 启动/停止APP
//启动
d.app_start("com.android.browser") #默认的这种方法是先通过atx-agent解析apk包的mainActivity,然后调用am start -n $package/$activity启动
d.app_start("com.android.browser", use_monkey=True) #使用 monkey -p com.example.hello_world -c android.intent.category.LAUNCHER 1 启动,这种方法有个副作用,自动会将手机的旋转锁定给关掉
//停止
d.app_stop("com.android.browser")
d.app_clear('com.android.browser')
2.2.4 元素定位方法
d(resourceId="com.smartisanos.clock:id/text_stopwatch").click() #ResourceId定位
d(text="发现").click() #Text定位
d(description="..").click() #Description定位
d(className="android.widget.TextView").click() #ClassName定位
2.2.5 控件操作
d.click(x, y) #点击
d.double_click(x, y) #双击
d.double_click(x, y, 0.1) # 两次点击时间间隔0.1秒
d.long_click(x, y) d.long_click(x, y, 0.5) # 点击时间0.5秒,长按
python端:运行脚本,并向移动设备发送HTTP请求。移动设备:移动设备上运行了封装了uiautomator2的HTTP服务,解析收到的请求,并转化成uiautomator2的代码,对被测程序进行相关操作。
以下以计算器为例,计算七加八并自动获取计算结果。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import uiautomator2 as u2
import time
if __name__ == '__main__':
d = u2.connect_usb("123456f") # 通过USB连接手机(手机的序列号可以通过adb devices获取到,假设序列号是123456f)
d.app_start("com.coloros.calculator") # 通过包名启动手机计算器(不同手机计算器包名可能不同,需要修改)
d(resourceId="com.coloros.calculator:id/digit_7").click() # resourceId定位法,点击“7”
d(resourceId="com.coloros.calculator:id/op_add").click() # 点击“+”
d(text="8").click() # text定位法,点击“8”
# d(resourceId="com.coloros.calculator:id/eq").click()
d.click(0.829, 0.92) # 坐标定位法,找到“=”的坐标点,点击“=”
d(resourceId="com.coloros.calculator:id/result").get_text() #获取result结果
time.sleep(5)
d.app_stop("com.coloros.calculator") #关闭手机计算器
同时控制手机和音箱,配合模拟人与手机助手的语音交互并保存返回的识别结果
控制手机打开语音助手
def openAssistant()
d = u2.connect_usb("手机序列号")
d.app_start("包名", "activity", use_monkey=True) #启动手机语音助手,需要通过语音助手包名启动
d.click(x, y) #通过坐标定位方法,点击语音助手浮球,开启语音助手交互
播放测试音频文件
# pcm音频读取
class PcmRead:
def __init__(self, path, sampleRate, sampleSize, channelCount):
self._i_opened_the_file = None
if isinstance(path, str):
f = open(path, 'rb')
self._i_opened_the_file = f
# else, assume it is an open file object already
self._framerate = sampleRate
self._sampwidth = sampleSize
self._nchannels = channelCount
self._nframes = os.path.getsize(path) // (sampleSize * channelCount);
def __del__(self):
self.close()
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def close(self):
file = self._i_opened_the_file
if file:
self._i_opened_the_file = None
file.close()
def getnchannels(self):
return self._nchannels
def getnframes(self):
return self._nframes
def getsampwidth(self):
return self._sampwidth
def getframerate(self):
return self._framerate
def readframes(self, nframes):
size = nframes * self._sampwidth * self._nchannels;
data = self._i_opened_the_file.read(size);
return data
#播放音频文件
def play(audioPath):
if audioPath.endswith(".pcm"):
wf = PcmRead(audioPath, "音频采样率", "采样点大小,只支持2", "音频声道数")
elif audioPath.endswith(".wav"):
wf = wave.open(audioPath, 'rb')
else:
raise ValueError("invalid suffix")
with wf:
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
CHUNK = 1024
data = wf.readframes(CHUNK)
while b'' != data:
stream.write(data)
data = wf.readframes(CHUNK)
stream.stop_stream()
stream.close()
p.terminate()
uiautomator2+python UI自动化测试框架,与selenium 和 unittest 的 Web UI自动化测试框架相类似,基于Android系统有屏设备的自动化测试解决方案,支持对被测设备的模拟点击、截图、获取返回结果等功能。可用于所有Android带屏设备的APP测试。