众所周知,安卓单台设备的 UI 自动化测试已经比较完善了,有数不清的自动化框架或者工具。但是介绍多设备管理的内容并不多,当手里的手机多了之后,要做自动化测试平台,这块的东西又不得不碰,摆脱 USB 限制,接入 WiFi,才能更加自由

框架介绍

1.ATX

ATX(AutomatorX) 是一款开源的自动化测试工具,支持测试 iOS 平台和 Android 平台的原生应用、游戏、Web 应用。 使用 Python 来编写测试用例,混合使用图像识别,控件定位技术来完成游戏的自动化。附加专用的 IDE 来完成脚本的快速编写。

atx-agent运行在手机的内部,为手机增加了远程控制,自动化的功能。atx-server最重要的功能,是将atx-agent汇总到一个网页上展示,并提供一个 API 可以获取所有设备的列表

2.uiautomator

UIAutomator 是为数不多的 Android 官方支持的自动化框架之一,UIAutomator 随着 Android 版本发布而更新。作为基于控件的自动化框架,UIAutoamtor 的整体框架及 API 简单明晰,非常容易上手,发布后便受到了不少开发人员的好评,但仍有部分开发人员觉得不支持 resourceId 检索控件有点儿可惜。官方在随后的 Level 18 中弥补了这一缺憾,至此,UIAutomator 便在自动化测试领域占据了一席之地,满足了大部分开发人员的需求。
UIAutoamtor 较于其他自动化框架有什么特性呢?笔者觉得可以用粗暴但灵活、简单可依赖来形容,其细数的优势有很多,
概括起来有以下几点

官方支持更新: Android 原生支持,测试依赖环境少,创建方便。·

层次接口明晰: 框架层次结果分明,API 明晰,上手成本很低。

基于控件交互: 支持 Android 原生控件解析,比坐标交互兼容性更强。

不依赖于源码: 测试过程基于黑盒进行,对所有发行版本都可以测试。

事件等待优秀: 在事件等待方面接口丰富,控制灵活精确,表现优秀。

支持跨进程测试: 在自动化框架中,具备此特性的不多,测试范围在 ROM。

在技术选型方面,除了涉及 Web 的测试,UIAutomator 基本上都可以帮用户实现。如果用户想做 ROM 层级的测试,或是 App 间
协作需跨进程的测试,那 UIAutomator 将是非常好的选择;用户渴望写出简单易懂而功能强大的代码,也不妨选择一
试,UIAutomator 可以让用户在事件等待方面看到它优雅的一面;没有代码没关系,想要竞品对比也可以,单元测试、性能测
试、压力测试,UIAutomator 都可以成为用户的选择;简单而不简约,留给开发人员更自由的发挥空间,轻巧灵动,强大可靠,
这就是 UIAutomator

3.python-uiautomator2

python-uiautomator2 封装了谷歌自带的 uiautomator2 测试框架,提供便利的 python 接口。他允许测试人员直接在 PC 上编写 Python 的测试代码,操作手机应用,完成自动化,大大提高了自动化代码编写的效率。

如图所示,python-uiautomator2 主要分为两个部分,python 客户端,移动设备

整个过程

  1. 在移动设备上安装atx-agent(守护进程), 随后atx-agent启动 uiautomator2 服务 (默认 7912 端口) 进行监听

  2. 在 PC 上编写测试脚本并执行(相当于发送 HTTP 请求到移动设备的 server 端)

  3. 移动设备通过 WIFI 或 USB 接收到 PC 上发来的 HTTP 请求,执行制定的操作

API 介绍

github:https://github.com/openatx/uiautomator2

中文翻译:https://blog.csdn.net/qq_38071435/article/details/80003212

1、安装:

pip install --pre uiautomator2
或者你可以直接从github上源码安装
git clone https://github.com/openatx/uiautomator2
pip install -e uiautomator2
pip install pillow  #截屏工具

2、初始化手机,需要的环境 SDK

python -m uiautomator2 init
这个只有初始化后才可以用
pip install --pre --upgrade weditor#安装自动化UI定位
python -m weditor#启动

自动安装本库所需的设备端程序:uiautomator-server ,atx-agent ,openstf / minicap ,openstf / minitouch  可单独下载安装

配置工作完成

3、打开手机端口:adb forward tcp:7912 tcp:7912 

     连接手机:device_ip = 127.0.0.1 

    这个不知道干嘛的忘记了好像是是自行安装 atx-agent 的时候用的

4、常用命令:

1安装apkpython -m uiautomator2 install $ device_ip https://example.org/some.apk
2清缓存python -m uiautomator2
3停止所有应用程序python -m uiautomator2 app-stop-all $ device_ip
4截图python -m uiautomator2截图$ device_ip screenshot.jpg
5检查守护线程d.healthcheck()
6打开调试d.debug = true
7获取连接信息d.info
8shell命令d.adb_shell'pwd'
9分辨率d.window_size()
10查看当前应用信息d.current_app()
11查看序列号d.serial

5、快速开始:

import uiautomator2 as u2
通过WIFI
d = u2.connect'10 .0.0.1')#u2.connect_wifi'10 .0.0.1'别名 
通过usb获取设备
d = u2.connect'123456f')#u2.connect_usb'123456f'别名

6、系统操作:

1安装只指出urld.app_install'http://some-domain.com/some.apk'
2启动d.app_start(“com.example.hello_world”)#以包名称开头
3停止应用d.app_stop(“com.example.hello_world”)
            d.app_clear'com.example.hello_world'
        停止所有应用d.app_stop_all()
5推送一个文件到手机d.push(“foo.txt的”,“/ SD卡/”)
6推和重命名d.push(“foo.txt的”,“/sdcard/bar.txt”)
7推送并更改文件模式d.push(“foo.sh”,“/ data / local / tmp /”,mode = 0o755
8从设备中拉取文件d.pull(“/ sdcard / tmp.txt”,“tmp.txt”)#如果文件在设备上找不到FileNotFoundError将会出现 
    d.pull(“/ sdcard / some-file-not-exist.txt”,“tmp.txt”)

7,应用连接会话:

1启动应用sess = d.session(“com.netease.cloudmusic
2会话连接运行中的程序sess = d.session(“com.netease.cloudmusic
3检测应否崩溃SESS文字= 音乐”)点击()
    检查会话是否正常sess.running()

8,手机硬件操作

1d.screen_on()#打开屏幕d.screen_off()#关闭屏幕
2获取当前屏幕状态d.info.get'screenOn')#android 4.4
3按软/硬件
dpress(“home”)#按home键用键名 
dpress(“back”)#按返回键与主要的名称 
dpress0x070x02)#按下键码0×07(“0”)与META ALT0x02
dunlock()解锁屏幕

实践代码

所用到的设计模式

PageObject 设计模式:

1.是将某个页面的所有"元素(包含控件)属性"及"元素操作"封装在 1 个类 (Class) 里面~~~~

  1. 目的: 测试代码与被测页面对象代码分离,后期如果有页面元素发生了更改,只需要修改相应页面对象的代码 (即对应 Class 文件),而不需要修改测试代码
  2. 尽量采用 xpath 方式来寻找页面元素,而不建议使用 name,Link 等方法; xpath 是基于页面元素所处区域,一般不会发生变化,测试代码基本不受干扰.
  3. 将页面元素属性信息与代码分离,即与被测对象代码分离,目的也是为了进一步降低后续因页面变化带来的维护成本
def install(d):#安装元素选择
    lists = ["com.kingroot.kinguser:id/button_right", "vivo:id/vivo_adb_install_ok_button", "android:id/button1",
             "com.android.packageinstaller:id/decide_to_continue"]
    while True:
        for i in lists:
            anzhuang(i,d)
def anzhuang(string,d):#元素触发
    try:
        s = d(resourceId=string)
        if s.exists:
            s.click()
    except:
        pass

从 ATX-server 中获取当前可用设备列表

1.在 ATX server 自带的设备管理页面中,列出了当前可用的设备列表。于是尝试从这个页面的网络请求中获取设备列表,果然有收获:

从 list 这个请求中,通过 present 属性,获取当前可用的设备列表,就可以逐个调用进行测试了。

def getDevices():#获取在线ip
    url=r'http://192.168.1.110:8000/list'.encode()
    r=requests.get(url)
    deviceList=[]
    content=json.loads(r.text)
    for device in content:
        if device['present']:
            deviceList.append(device['ip'])
            print(device['ip'])

python 启动以及关闭服务

开启服务
obj1=subprocess.Popen(['rethinkdb','--http-port','8090'],cwd=r'C:\rethinkdb-2.3.6',shell=True, stdout=subprocess.PIPE)
obj2=subprocess.Popen(['atx-server','--port','8000'],cwd=r'C:\Users\外网\go\src\github.com\openatx\atx-server',shell=True,stdout=subprocess.PIPE)
obj3=subprocess.Popen(['python','-m','http.server','8888'],cwd=r'C:\Users\外网\Desktop',shell=True,stdout=subprocess.PIPE)
关闭服务
subprocess.Popen('TASKKILL /F /IM rethinkdb.exe', shell=True, stdout=subprocess.PIPE)
subprocess.Popen('TASKKILL /F /IM atx-server.exe', shell=True, stdout=subprocess.PIPE)
subprocess.Popen('TASKKILL /F /IM python.exe',shell=True, stdout=subprocess.PIPE)

框架 API 使用

iplist=getDevices()
for ip in iplist:#
    d = u2.connect('%s'%ip.strip())
    d.unlock()
    d.healthcheck()
    d.app_uninstall_all()
    az=threading.Thread(target=install,args=(d,))
    az.start()
    package=d.app_install('http://192.168.1.110:8888/T11.apk')
    #d.disable_popups()
    print(package)
    d.app_start(package)


↙↙↙阅读原文可查看相关链接,并与作者交流