ATX 小白入门篇:python uiautomator2 的代码示例 (包含 app 管理,session 控制,toast,htmlreport,unittest ..)

codeskyblue · 2018年03月26日 · 最后由 codeskyblue 回复于 2020年07月21日 · 11033 次阅读

简介

你是否曾经想过有一天你手里的手机在你不动它的情况下,自己点来点去,打开各种 app,操作来操作去。其实这一天已经到来,而且你还可以按照自己的想法,让手机自动化的去操作。
要想让手机实现自动化,你可能需要学习一点 Python,这是一门简单易懂的计算机编程语言,非常容易学习和编写。如果你有其他语言的基础,一天学习下来,差不多可以拿 Python 写个小程序了。这门语言发展了十多年,数不清的 Python 使用者为 Python 贡献了无数多的库,有了合适的库,就仿佛战场上的你有了一个趁手的兵器,可以瞬间增加战斗力。
今天这篇文件介绍的也是一个库,名字是 uiautomator2, 在https://pypi.python.org/pypi/uiautomator2 上可以看到。

安装

适用范围
Android 手机 4.3+(sdk 18)

从 Python 官网https://python.org下载安装完之后(推荐 Python3),使用Win + R快捷键,输入cmd,然后回车,可以看到一个命令行窗口。(国际惯例,我把名字模糊了

库的安装和升级

命令行窗口输入pip install -U --pre uiautomator2经过短暂的等待,就安装好了。部分情况可以因为中国互联网防火墙的原因,安装不了,这是可以试试用国内的 pypi 镜像

pip install -U --pre uiautomator2 -i https://pypi.doubanio.com/simple

如果需要更新这个库,重新运行一下这个命令就可以。

初始化设备

uiautomator2 库通过 http 协议与手机上的服务通信,完成我们想要的自动化。手机上的服务是我们自己实现的,原本是没有的,所以在测试之前,我们需要做一下预处理。

预处理需要向手机上安装 5 个组件,分别是

  1. minicap 用于手机的快速截图
  2. minitouch 用于远程控制(这篇文章暂时不讲,文章最后会有相关链接)
  3. app-uiautomator.apk (UiAutomator2 服务,Toast)
  4. app-uiautomator-test.apk(UiAutomator2 服务)
  5. atx-agent (可以直接在安卓上运行的二进制程序)

一个个的手动安装未免有点麻烦,所以我们提供了更快捷的方法。
首先将设备插入到手机上,如果提示信任开发者选项就点击确认。

# 先确认下是否可以看到可用设备
$ adb devices
List of devices attached
3578298f        device

# 开始将这5个零件安装到手机
$ python -m uiautomator2 init
2018-03-22 10:14:09,218 - __main__.py:268 - INFO - Detect pluged devices: ['3578298f']
2018-03-22 10:14:09,218 - __main__.py:284 - INFO - Device(3578298f) initialing ...
2018-03-22 10:14:09,722 - __main__.py:113 - INFO - install minicap
2018-03-22 10:14:09,943 - __main__.py:120 - INFO - install minitouch
2018-03-22 10:14:10,361 - __main__.py:137 - INFO - apk(1.0.12) already installed, skip
2018-03-22 10:14:10,460 - __main__.py:175 - INFO - atx-agent(0.2.1) already installed, skip
2018-03-22 10:14:10,700 - __main__.py:212 - INFO - launch atx-agent daemon
2018-03-22 10:14:13,711 - __main__.py:228 - INFO - atx-agent output: server started, listening on 10.242.62.224:7912
2018-03-22 10:14:14,459 - __main__.py:232 - INFO - success

安装过程中会联网下载组件,安装到最好当看到提示success的时候问题就不大了。
如果安装失败,可以加 QQ 群 497460177, 或者查看 FAQ 贴 https://testerhome.com/topics/12025 通常来说都不是什么大问题。

Hello world 1

接下来看一段 Python 脚本

# coding: utf-8
import uiautomator2 as u2

u = u2.connect_usb()
u.make_toast("Hello world", 3)

运行这段代码,手机上会发现有一台Hello world消息显示 3s 后消息。部分手机需要设置下运行悬浮窗的功能,比如小米。
参考该链接 百度经验 小米手机打开应用悬浮窗,允许 uiautomator 应用显示悬浮窗

如果这一步成功了,恭喜你,你即将跨入自动化测试的大门。

Hello world 2

手机先下载一个网易云音乐,下载链接http://music.163.com/#/download。手机上有的就不用再安装了。

# coding: utf-8
import uiautomator2 as u2

u = u2.connect_usb()
sess = u.session("com.netease.cloudmusic") # 启动网易云音乐
sess(text="私人FM").click()

session
u.session函数会先重启应用,返回 session object,如果应用闪退,通过 session object 进行的所有操作都会抛出异常。
如果不希望 session 函数杀掉应用,可以这样 sess = u.session("com.netease.cloudmusic", attach=True)

sess(text="私人 FM").click()
查找界面上组件的 text 为私人FM的组件,然后执行点击操作

Hello world3

通过刚才的两个 Hello world,相信你已经稍微认识了 uiautomator2,下面我们给代码加入更多的功能。

# coding: utf-8
import unittest
import uiautomator2 as u2
import time
import uiautomator2.ext.htmlreport as htmlreport

class TestCloudMusic(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.u = u2.connect_usb()
        cls.u.healthcheck()  # 解锁屏幕并启动uiautomator服务
        hrp = htmlreport.HTMLReport(cls.u, 'report')
        hrp.patch_click()

        # cls.u.disable_popups(True)  # 允许自动处理弹出框
        cls.u.make_toast("测试开始", 3)

    @classmethod
    def tearDownClass(cls):
        cls.u.make_toast("测试结束", 3)
        cls.u.app_stop_all()
        cls.u.service(
            "uiautomator").stop()  # 停止uiautomator守护程序,允许其他测试框架如 appium 运行

    def setUp(self):
        self.d = self.u.session("com.netease.cloudmusic")  # restart app
        time.sleep(5)  # 等待首页广告结束

    def tearDown(self):
        pass

    def testPrivateFM(self):  # 私人FM
        self.d(text="私人FM").click()

    def testRecommendEveryday(self):  # 每日推荐
        self.d(text="每日推荐").click()


if __name__ == '__main__':
    unittest.main()

这个例子更加完善,新增了 unittest 集成,htmlreport 插件的集成以及 uiautomator service 相关的控制代码。

unittest

unittest 是 python 自带的单元测试库。新建一个类继承自 unittest.TestCase。类里面我们写了两个以test开头的函数,我们称这两个函数为测试单元。
当我们运行unittest.main()的时候,这些测试单元会被依次调用。其中的setUp函数在每个测试单元调用之前被调用,而tearDown则在之后被调用。
setUpClass函数在所有测试单元调用完之前调用,相应的tearDownClass在所有测试单元调用完之后调用。

healthcheck 和 d.service("uiautomator").stop()

healthcheck函数相当于飞机起飞前的自检。因为我们的 uiautomator 库依赖于手机上运行的 UiAutomator 服务,所以运行之前最好能够检查一下。
最后的d.service("uiautomator").stop()是因为,安卓上的 UiAutomator 是独享的,一旦一个服务使用了它,其他人就不让碰了。所以 appium, macaca, uiautomatorviewer.bat 只要你用了 UiAutomator 服务,都是冲突的。只有再用完之后,停止掉 uiautomator service,才能让其他服务使用。

如果你只用我们这一个库,也是可以不用 stop 掉这个 service 的。

htmlreport

htmlreport 是为了方便记录测试结果写的一个扩展。想知道实现的人可以看看源码https://github.com/openatx/uiautomator2/tree/master/uiautomator2/ext/htmlreport
测试运行完之后,会在代码运行目录下生成一个 reoprt 目录。

因为浏览器限制的原因,直接双击打开的 html 不能加载本地的 json,所以必须要一个简单的文件服务器。双击start.bat就算是启动了一个简易的 python 文件服务器。
浏览器可以看到一个简单的包含截图的测试记录

app_stop_all

这个函数相当于手机回到桌面后,关闭后台进程。

进阶

为了方便的写代码,我们还要用到另外一个项目weditor, 可以很方便的查看当前界面的元素信息,写起脚本来飞快。

点击这里继续阅读

相关链接

项目地址 https://github.com/openatx/uiautomator2

  1. ATX 安卓设备集群管理 atx-server https://testerhome.com/topics/11546
  2. 浅谈自动化测试工具 https://testerhome.com/topics/11357
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 22 条回复 时间 点赞

前排点赞

顶,还是老大写的详实仔细

看完后又去了解了下 unittest。哈哈,读完老大的帖子,又学到了。开心

linpengcheng ATX-uiautomator2 实现 webview 的操作 中提及了此贴 03月29日 21:08

向大神求助:python -m uiautomator2 init 输入后,最后提示:EnvironmentError: package com.github.uiautomator version expect "1.0.12" got "None" ,应该怎么破?这些组件都安装不成功!

稻草人 回复

先看看 apk 是否允许 adb 安装

你好,这边执行后提示没有效果,不知道是哪边出了问题。代码如下
import unittest
import uiautomator2 as u2
import time
import uiautomator2.ext.htmlreport as htmlreport

class ypp(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.u = u2.connect_usb('192.168.2.35')
cls.u.healthcheck() # 解锁屏幕并启动 uiautomator 服务
hrp = htmlreport.HTMLReport(cls.u, 'report')
hrp.patch_click()

# cls.u.disable_popups(True) # 允许自动处理弹出框
cls.u.make_toast("测试开始", 3)

@classmethod
def tearDownClass(cls):
cls.u.make_toast("测试结束", 3)
cls.u.app_stop_all()
cls.u.service(
"uiautomator").stop() # 停止 uiautomator 守护程序,允许其他测试框架如 appium 运行

def setUp(self):
self.d = self.u.session("com.yitantech.gaigai") # restart app
time.sleep(5) # 等待首页广告结束

def tearDown(self):
pass

def login(self): # 登录
self.d(resourceId = "com.yitantech.gaigai:id/b3b").click()
self.d(resourceId = "com.yitantech.gaigai:id/d_").click()
self.d(resourceId = "com.yitantech.gaigai:id/d_").set_text("15050314133")
self.d(resourceId = "com.yitantech.gaigai:id/bp").click()
self.d(resourceId = "com.yitantech.gaigai:id/d_").set_text("xiaowang52")
if name == 'main':
unittest.main()

10楼 已删除
11楼 已删除
12楼 已删除

大神允许自动处理弹出框 ,这种情况一用就报 NotImplementedError,只要是弹窗我用 click 方法都点不上
cls.u.disable_popups(True) # 允许自动处理弹出框

def disable_popups(self, enable=True):
"""
Automatic click all popups
TODO: need fix
"""
raise NotImplementedError()

if enable:
self.jsonrpc.setAccessibilityPatterns({
"com.android.packageinstaller": [u"确定", u"安装", u"下一步", u"好", u"允许", u"我知道"],
"com.miui.securitycenter": [u"继续安装"], # xiaomi
"com.lbe.security.miui": [u"允许"], # xiaomi
"android": [u"好", u"安装"], # vivo
"com.huawei.systemmanager": [u"立即删除"], # huawei
"com.android.systemui": [u"同意"], # 锤子
})
else:
self.jsonrpc.setAccessibilityPatterns({})

李文珊 回复

这个方法有点问题,还没实现完

codeskyblue 回复

嗯,谢谢 codeskyblue ,那么像这样的弹窗也是需要用 disable_popups 方法才能点击吗,我直接用 click 方法,不能点击,也没有报错,这种弹窗要怎么处理的?

李文珊 回复

你试试 long_click

codeskyblue 回复

我想在init新增函数 long_click_exists,但是 调用 long_click_exists 报错 UiObject 没有这个属性,codeskyblue,我要怎么样才能加上自定义的函数的
def long_click_exists(self, timeout=0):
try:
self.long_click(timeout=timeout)
return True
except UiObjectNotFoundError:
return False

李文珊 回复

看起来没啥问题

codeskyblue 回复

只要调用就报错

codeskyblue 回复

codeskyblue 不知道是不是我用的方式不正确,直接截图上所示

李文珊 回复

UIObject 对象是一个类似 element 对象,如果你要对这个元素进行操作你需要提取其中的 text 或者其他元素

@bbcat我不太理解你说的,可以给我写下例子吗,感谢

李文珊 回复

不好意思,我看错了。没看到你问的是新增函数 却不能调用。
如果你需要问下作者,可以去加下 Q 群。这样沟通起来可能会更方便

linpengcheng 基于 ATX-Server 的 UI 自动化测试框架 中提及了此贴 06月18日 22:20
27楼 已删除
codeskyblue 专栏文章:2018年 终总结 中提及了此贴 02月18日 10:26
codeskyblue 回复

大佬,为啥我第二步执行就报错了,我搞了好久也没搞好,能否帮忙看下?

d.app_start('package_name', stop=True, wait=True)
d.session('package_name')

这两个有什么区别,session 的应用场景是什么?

yueyeKIDL 回复

没啥区别了,现在已经保持一致了

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册