篇 11 python 自动化测试应用-python 调用安卓 adb 命令(下篇)
--lamecho 辣么丑
1.1 概要

大家好! 我是 lamecho(辣么丑),今天是《python 自动化测试应用》的第十一篇,本篇将继续第十篇的 adb 命令结合 pyapp 框架编写过程中实际遇到的那些坑。后面的内容不是简单的列举命令(因为网络上大把,大家关心什么可以自己搜索),而是一贯 lamecho 辣么丑的风格,实战。好了各位同学坐稳了,我要发车了,哈哈。

1.2 那些需要注意的 adb 命令

1.2.1 “adb shell input ……”

这条命令,在测试过程中也是经常用到的,它后面可以跟 tap,text,swipe,进行点击屏幕,输入文本,滑屏的操作,具体在 python 中使用按照命令格式执行也不会出现什么问题。但是如果我们要长按某个元素,从而实现具体功能呢?下面给出实现代码。

def long_press(dev,data,hold_time):
action='adb -s '+dev+' shell input touchscreen swipe '+'%d'%data[0][0]+' '+'%d'%(data[0][1])+' '+'%d'%data[-1][0]+' '+'%d'%(data[-1][1])+' '+hold_time
print action
pi= subprocess.Popen(action,shell=True,stdout=subprocess.PIPE)

long_press('4d0041b1be98b01f',[[540,716],[545,718]],'1000')

可以看到我定义的 long_press 方法,action 里用到的仍然是 swipe 命令,大家知道 swipe 是滑动屏幕的操作,那么如果我们在传递滑动范围坐标的时候,设定的滑动范围非常小,那么是不是就是间接的达到了长按某一个区域的目的,然后配合一个整个命令的执行时间的参数,是不是就完美解决了长按这个动作。
我们看一看完整的 adb 命令:
adb -s 4d0041b1be98b01f shell input touchscreen swipe 540 716 545 718 1000
解释:-s 后跟设备号 ,swipe 先传移动坐标范围 “540 716 545 718”,然后 1000 是长按的时间,1000 的单位是毫秒(注意)。
大家不要以为这条命令就这样介绍结束了,还没有。按道理来说 input 后面的 source 源在 swipe 命令里默认就是 touchscreen,所以一般我们在写这条命令时是可以省略 touchscreen 的,但是实际我在编写 pyapp 框架的时候(由于 pyapp 设计需要支持多台设备),拿了不同型号品牌(分辨率)的手机做适配,毕竟框架要照顾的范围要必须广要有一定的普适性。我发现,在 pyapp 的僚机模式下(就是一台主设备操控多台附属设备欢迎关入群” 631466916“,获取最新 pyswat 和 pyapp 程序),主设备的长按操作在有些副设备上实现不成功,开始我也是在命令里省略了 touchscreen。开始也想不到什么原因,因为命令来说也不复杂不存在写错的情况,毕竟有些副设备还是能正确响应动作的,后来我把 shell input 命令的帮助结果打印出来研究了一下(见上一篇文中有截图),在 swipe 命令的参数传递中说明了 touchscreen 是缺省,默认属性,所以正常来说我们是不用在命令中明确指定该参数值的,然而我也是瞎猫碰到死耗子,试着在命令里加入了 touchscreen,最后执行结果每一台副设备在长按命令的执行上都成功了。
1.2.2 adb 命令如何输入中文?

adb 命令里进行输入文本输入 ‘adb -s 设备号 shell input text 输入的内容’。在原生的 adb 命令里是不支持中文输入的,所以我们在测试的时候只能输入英文字符。然而实际我们在做 app 测试的时候避免不了需要输入中文字符的情况,这里给大家介绍一种曲线救国的办法。利用 “ADBKeyBoard” 输入法来进行中文的输入,通过广播的方式达到输入中文字符,具体命令:adb shell am broadcast -a ADB_INPUT_TEXT --es msg “内容”。

1.2.3 启动应用

在 appium 的应用中每次只能开启一个 app,而如果用 adb 命令的话就灵活许多,输入:adb shell am start –n package 名/.activity 名,这里的 package 名和 activity 名和 appium 中配置的一致。比如我们启动计算器程序,对应的命令就是 “adb shell am start -n com.android.calculator2/.Calculator”。这里要再提醒大家一点,APP 的 package 名和 activity 名一定要找对特别是 activity 名,具体的找发大家可以参看我的《python 自动化测试应用 - 第 2 篇(APP 测试)--Appium 初识篇》里边讲解了具体的查找方法。当然大家也是可以根据 adb 命令去进行查找,在 pyapp 测试框架中我就是根据 adb 命令进行名称的查找启动应用的。这里我讲解一下思路,利用命令 adb shell pm list packages -3,将手机中安装的第三方 app 列举出来,然后通过时间比对找到新安装的 app 从而确定名称。其实大家只要有了思路可以在百度中查找对应的命令即可。

1.2.4 只会发短信,那么查看短信呢?不会你就 out 了!

网上你可能找到如何发短信,打电话的相关 adb 命令的介绍,其实原理也就是 1.2.3 中介绍的,还是启动对应的应用程序来实现。比如发短息:adb shell am start -a android.intent.action.SENDTO -d smsto:发送号码 --es sms_body 短信内容。那么如何读取一条短信内容呢?
在编写 pyapp 框架的过程中为了实现 app 的验证码自动填写功能,着实费了一番功夫。目前大部分的注册登录都是可以用动态短信验证码来进行操作的,当然 app 开发者本身是可以实现在收到短信后读取验证码进行自动填写,遇到没有这种功能的 app 我们当然也可以按照此原理去实现。由于安卓手机的所有短信都是存储在数据库中,那么我们只要找到短信的这个数据库文件,自然就可以轻松的通过 python 的数据库操作读取到短信内容了。'/data/data/com.android.providers.telephony/databases/mmssms.db'这个路径下 “mmssms.db” 文件就是保存短信内容的数据库文件,那么剩下的工作自然就是数据库的读操作了,用正则表达式匹配到验证码即可,最后通过 adb 的 input 命令写入到 app 中即可。这里还需要注意的是,操作 “mmssms.db” 文件需要 root 权限,所以你要想在 pyapp 框架里使用这个功能必须是使用 root 过的手机设备。

1.2.5 adb 命令 su 权限如何使用?

什么是 adb 命令的 su 权限,举个简单的例子,比如说你要访问手机的内存/data/system 路径下的文件。在 cmd 命令窗中你需要执行三步:
1.在 cmd 窗体中输入命令:adb shell 回车

2.输入 su 回车

可以看到命令符由 $ 变成了 # 符号。
3.输入 cd /data/system

这样就完成了/data/system 路径的访问,如果遇到那些需要权限的文件你没有执行 su 的话,可能就会给你返回一句 Permission denied(权限拒绝)。
好了,说了这么多上面都是在 cmd 窗口中去分步执行的,那么在 python 中怎么去实现呢?如果按照之前讲解的方式在 python 脚本中分别执行这三条命令,肯定是不行的。因为程序执行一次 adb 命令,就会建立一个独立的进程,所以我们在脚本中不能按顺序执行三次 adb 命令,这样是达不到效果的。我们必须让一条 adb 命令一次完成所有的步骤的执行才对,那么我们这条命令该怎么写呢?简单暴力点,像这样:adb shell su cd data\system
肯定是不行的,不过大致意思是对的,只是具体写法上要按照正确的格式在 su 后面加上-c 就行了。如:adb shell su –c cd “data\system”。我们看看这条命令,注意以后需要 su 权限的 adb 命令都可以这样写,-c 后面跟着具体的操作命令即可。

1.2.6 uiautomatorviewer 和 hierarchyviewer 傻傻分不清楚?
大家在做 android 自动化测试时,必定会需要知道界面元素/控件的相关属性,如 id,class 等,这时在 androidsdk 的 tool 工具中就会用到 uiautomatorviewer 和 hierarchyviewer。这两个工具都可以很直观的方便大家去查找界面元素,而这样只是单纯的人工借助工具去查看,既然是要做自动化测试,必然我们要去通过代码编程去代替手工操作了。在 pyapp 框架中,我也是利用 uiautomatorviewer 来捕获 app 界面的从而获取对应的元素/控件。接下来我们就来看看 python 是如何做到的。相对来说 uiautomatorviewer 的实现更容易些,一条 adb 命令就可以了。
order='adb -s device shell uiautomator dump'
运行成功后会返回:
“UI hierchary dumped to: /storage/emulated/legacy/window_dump.xml“
结果很直观了,这个 window_dump.xml 里就是整个界面的布局层级信息,从中我们就可以获取到各个元素/控件的属性信息。如果利用浏览器打开 xml 文件大家也可以直观的看到界面的层级关系,如果大家只关心具体的元素控件,我们可以在命令后面加上—compressed,这样获取的 xml 就会清爽许多。
我们接着看看怎么在 python 中利用 hierarchyviewer 实现元素/控件获取,首先我们要知道要实现 hierarchyviewer 的元素获取我们要打开手机的 View Server 服务,并与其进行 socket 通信,从而获取到元素/控件信息。那么接下来我们将通过几条不同的 adb 命令来准备好与 View Server 进行通信的环境。
第一步:
通过 “adb shell service call window 3” 命令得到返回值 Result: Parcel(00000000 00000001 '........') 或者 Result: Parcel(00000000 00000000 '........') 如果是 00000001 表示 View Server 是开启的,反之我们就要开启 View Server 服务。
第二步:
如果 View Server 没有打开,我们就通过 “adb shell service call window 1 i32 4939” 命令打开。然后再通过第一步的命令确认是否开启了 View Server。
第三步:
当我们开启了服务后,需要再将手机的 4939 端口映射到电脑的 4939 端口,这样就可以进行 socket 通信了。命令为 “adb forward tcp:4939 tcp:4939”
以上这三步完成后,接下来我们就可以在 python 脚本中与 View Server 建立 socket 通信了。由于已经有服务端 View Server,我们只需要用 python 实现客户端的 socket 代码即可。
import socket# 导入 socket 模块
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 建立 TCP 连接
sock.connect(('127.0.0.1',4939))# 连接服务端地址及端口并建立连接
sock.send('list')# 发送 list 命令向服务端
sock.recv(1024)# 接收返回结果
这里就不去讲解 python 的 socket 用法了,重点讲解一下这里的 list 指令。当我们建立好与 View Server 的通信后,就可以发送不同的指令向服务端,获取信息了,这里的 list 意思就是获取当前手机活动界面的信息。如下图

有了这个信息,我们就可以继续发送 dump 命令去获取具体某个界面的详细层级信息了。举个例子比如我们要打印出上图中第二行
“432b8608 com.miui.home/com.miui.home.launcher.Launcher”
的界面信息,在我们发送内容中应该这样写 sock.send(“dump 432b8608”),获取结果如下图。由于内容会比较多,建议大家在代码中将返回值写入 txt 文件中,方便查看。具体的内容中每一段表示一个元素,里边详细的列举了元素的各种属性信息,如 id,class,坐标等。

至此,我们了解到通过发送 list,dump 两个命令可以进行元素信息的获取。另外还有一个 capture 命令可以获取到元素在界面中的截图,这里就不再赘述了。至于到底是用 uiautomatorviewer 还是 hierarchyviewer 去实现元素的获取,决定权就在你的手上了。
好了,有关 python 自动化测试应用 - 第 11 篇(APP 测试)之 adb 命令 (下篇) 就写到这里。本篇着重讲解了一些非常规的 adb 命令的使用,尽可能的贴住适合我们做自动化测试相关的 adb’ 命令使用,或者说是针对我在写 pyapp 时实现的一些比较实用的 adb’ 命令。最后再声明本文不去列举 adb 命令,网上资料有很多,最后希望本篇文章能够帮助到大家。我是你们的 lamecho 辣么丑。
同时欢迎大家下载使用 pyswat,pyapp 自动化测试框架。

原创文章,转载请注明出处。
欢迎关入群” 631466916“,获取最新 pyswat 和 pyapp 程序
微博:https://weibo.com/u/6017986584


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