八、移动测试
- java和Android环境的安装和配置
- Android模拟器的配置
- adb命令概念及原理
- 常用的adb命令
- Appnium的介绍及安装配置
- Appnium中常见的测试操作
- Appnium+nuittest框架应用
(一)移动测试概念
1)定义
测试手机程序
2)测试的方面
- 功能测试:查看功能是否正常
- 安装卸载测试
- 升级测试
- 兼容性测试
- Android系统版本
- 厂商二次开发版本
- 不同分辨率
- 不同网络
- 网络切换、中断测试
- 使用中来电话、短信
- 横竖屏切换
- 健壮性:耗电性、流量消耗、崩溃回复
(二)环境搭建
1.java jdk
- 安装
- 配置环境变量
新建JAVA_HOME: C:\Java\jdk
新建CLASS_PATH: .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar
PATH: %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin
2.Android SDK
- 解压(路径不含中文及特殊字符和空格目录下)
- 配置环境变量
新建ANDROID_HOME,并将第一步放置的sdk路径配置到该环境变量中
在PATH里面添加: %ANDROID_HOME%\platform-tools;%ANDROID_HOME%\tools
系统变量>>双击Path>>编辑环境变量
配置path:%ANDROID_HOME%\platform-tools
%ANDROID_HOME%\tools
验证是否配置成功
1、cmd打开命令窗体,输入android命令
2、在cmd界面输入adb
版权声明:本文为CSDN博主「yopky」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
配置好后,可以在命令行里面运行adb,可以出现命令提示即可。
3.虚拟机安装
使用夜神模拟器
也可以使用真机
- 手机通过usb线连接到电脑
- 在手机上打开文件传输(通知栏,手机正在充电)
- 在电脑上会发现多了一个CD驱动器,里面时手机的驱动
- 安装驱动,电脑的设备管理器中就可以看到型号了
- 开启开发者模式:手机-设置-关于手机-版本号(多次点击)
- 打开usb调试:手机-设置-开发者选项-usb调试(打开)
MUMU模拟器:http://mumu.163.com
mumu模拟器12操作:https://mumu.163.com/help/20230214/35047_1073151.html
安装使用问题:
1、此应用无法在你的电脑上运行
在以下路径进入cmd
~:\Program Files\MuMu\emulator\nemu\vmonitor\bin
2、打开成功,但是adb devices 查询不到,且无法正常使用
List of devices attached
adb server version (32) doesn't match this client (40); killing...
adb F 02-16 23:02:07 7180 15148 main.cpp:45] cannot open D:\Program Files\Tmp\adb.log: Permission denied
could not read ok from ADB Server
* failed to start daemon
error: cannot connect to daemon
打开以下路径:
~:\Program Files\MuMu\emulator\nemu\vmonitor\bin
右键点击属性->兼容性,勾选管理员权限打开
如果不行,则将sdk安装目录下的adb替换adb_server.exe,并重命名为adb_server.exe。
(三)ADB
1)ADB概念及原理
关系:
会在电脑上创建处理一个服务(ADB服务),手机连上电脑后,都会与服务建立连接,要想发送指令给手机,需要把指令发送到服务中。
作用:
通过命令行去操作手机。
ADB命令原理:
ADB是Android Debug Bridge的简称,是android平台的调试工具。Android开发工程师必须掌握,测试工程师在作android测试工作时也会用到,需要掌握。
ADB包含三部分:
- Client端:Client本质上就是Shell,运行在开发机中,即开发电脑,用例发送ADB命令
- Server端,同样运行在开发机器中,用来管理Client端和手机端的Deamon之间的通信
- Deamon守护进程:运行在调试设备中,手机或模拟器,用来接收并执行ADB命令。
ADB工作过程:
- client端通过命令发送给server端
- server端会将命令发送给deamin端
- deamon端进行执行
- 将执行结果返回给server端
- server端将结果返回值给client端
2)常用ADB命令
命令:重要
- 连接adnroid手机或模拟器
在输入其他的ADB命令之前,应该先是应用ADB连接android手机或模拟器。
注:
真机连接时,需要先安装对应手机的手机驱动程序,打开开发者模式允许USB调试,安装完毕后可通过USB和wifi两种方式进行连接。虚拟机上打开开发者选项的操作:设置->关于平板电脑->版本号(快速点击3次以上),然后再去开发者选项打开USB调试。
MUMU模拟器使用介绍:http://mumu.163.com/help/20210531/35047_951108.html
链接adb需要在CMD运行框中将路径切换到
~:\Program Files\MuMu\emulator\nemu\vmonitor\bin
注:~代表模拟器所在安装路径的文件夹
问题诊断可以查看ADB调试 端口,即是7555位置的端口
之后输入以下代码:
adb_server.exe connect 127.0.0.1:7555
adb connect 127.0.0.1:16384
adb devices #列出当前已连接的所有设备,证明连接成功
adb_server shell
exit
预处理bat:
-
服务(如果发现代码连不上手机了)
adb kill-server #关闭服务器
adb start-server #打开服务器
-
查看设备
adb devices #列出当前已连接的所有设备。
adb connect IP地址:端口 #连接
mumu: adb_server.exe connect 127.0.0.1:7555
mumu模拟器端口时7555
夜神模拟器端口:6201
本机IP:127.0.0.1
-
进入手机终端
adb shell
问题:
adb端口为5037,如果发现adb无法启动,可以通过下面命令查询占用端口的进程
netstat -ano | findstr 5037
taskkill -f -pid pid号 #杀掉对应的进程,释放端口
-
获取信息
绿色是当前运行的程序activity,红色是当前程序运行的activity所在的包。
获取手机android版本:
adb shell getprop ro.build.version.release
获取手机当前运行的程序和界面的名称:
adb shell dumpsys window windows | findstr mFocusedApp
包名:package name。用于唯一标识一个app的应用。(不是应用的名字,应用名字可以相同,单包名一定不同,也不是apk名字)
activity名:在android中每个界面叫一个activity,获取activity名其实就是获取界面名字。
应用场景:自动化测试需要通过代码的形式告诉手机测试点是哪个应用的哪个界面,可以通过这个命令获取。
- 查询apk包名和启动activity的名称
有apk包的情况下,使用aapt工具查看(在android sdk路径下的build-tools文件夹下)
aapt dump badging apk文件路径 | findstr package
aapt dump badging apk文件路径 | findstr launch #启动activity后查询
eg
aapt dump badging D:\Study\计算器.apk | findstr package
没有apk包的情况下,直接运行adb命令查看。
adb shell dumpsys activity | findstr Run
(注意大小写)
可将其路径配置到环境变量的path中,可在任何目录下直接打开使用。
- 文件传输
adb push 电脑上的文件路径名 手机上的位置
adb pull 手机上的文件路径 电脑上的位置
导出文件时注意一点,由于权限问题不呢个导出到电脑根目录(比如d盘,会报错),之呢个导出到某个具体文件夹内(文件夹需要事先存在)。
由于我就在文件目录下进入cmd,可不输入txt的本地目录
- 软件安装卸载
adb install 电脑上的apk文件路径 安装路径 #往默认连接的手机设备上安装apk包,注:apk包的文件名不能含有中文或特殊字符。
adb install -r apk包路径 #覆盖安装apk包
adb -s 127.0.0.1:7555 install apk包路径 #往指定连接设备安装apk包
adb uninstall 应用的包名 #卸载程序
- 获取其他信息
adb shell am start -w 包名/.启动名
- 查询本机所有的应用程序包
adb shell pm list packages #本机所有应用程序包
adv shell pm list packages -3 #第三方应用程序包
- 截取当前页面
adb shell screencap 照片保存地址 #注意,只能跟跟手机里面的路径
eg:
adb shell screencap /sdcard/screen.jpg
adb pull /sdcard/screen.jpg d:\temp\screen.jpg
exit离开shell
查看手机有什么路径,可以adb shell后输入ls
文件一般传到sdcard目录下
更多命令(可下载):https://uploader.shimo.im/f/bsHDDsgkcrwKfjHP.xmind
(三)PyCharm+Appium
1、自动化测试工具
选取Appnium。支持语言多,支持的平台多。
Appium是一个开源、跨平台的自动化测试工具,用于测试原生和轻量移动应用,支持IOS、android和firefoxOS平台。Appium驱动Androud的UiAutomator框架,使用Selenium的webDriver JSON协议。
相比其他的移动自动化测试工具,Appium测试由于调用了Selenium的client库使其可以使用任意的语言,包括Python、Ruby、Node.js、Objective-C等。
Appium原理
Appium的核心是一个web服务器,提供了一套REST接口。它收到客户端的连接,监听命令,接着在移动设备上执行这些命令,然后将执行信果放在响http响应中返还给客户端。
特点:多语言编写测试代码;可以将服务器放在不同的机器上。
1)环境搭建:
1.安装appium的客户端
2.在python中安装Appium-Python库
安装appium-python库:pip install appium-python-client
参考:appium最全安装指南_appium安装-CSDN博客
更简洁: https://www.cnblogs.com/soundcode/p/12682366.html
注:启动appium客户端时必须右键选择“以管理员身份运行",否则在过程中会遇到权限问题。
需要打开ADB、模拟器、Appium、Pycharm
如果是之间导入的一个 自己在电脑中直接建立的文件夹,没有匹配对应的python环境。
2)启动设置
入门案例代码
#1.连接设备
#组装一个字典,注明平台,系统版本,名称
#从appium导入webderiver
from appium import webdriver
import time
#连接移动设备所必须的参数
desired_caps = {} # desired_capabilities 是字典,先定义一个空字典
#当前要测试的设备名称,可以写任意值,但不能weikong
desired_caps["deviceName"] = "127.0.0.1:7555" #mumu是7555
#必须参数,定义被测脚本的平台属性,不区分大小写
desired_caps["platformName"] = "Android"
#必须参数,定义被测系统的版本号(设置-关于本机- Android版本,必须根被测版匹配,不可乱写,大版本不可错,小版本可以不写
desired_caps["latformVersion"] = "6.0.1"
#必须参数,要启动的app的名称(app的唯一标识:包名)
desired_caps["appPackage"] = "com.android.settings"
#必须参数,要启动的app的界面
desired_caps["appActivity"] = ".Settings"
#不是必须,但一般需要指定。Uiautomator2是底层的一个引擎
desired_caps["automationName"] = "Uiautomator2"
#设置app重置策略。保持会话,不重置
desired_caps["noReset"] = True
#设置命令的超时时间
desired_caps["newCommandTimeout"] = 6000
#用于设置中文输入,两条同时设置
desired_caps["unicodeKeyboard"] = True
desired_caps["resetKeyboard"] = True
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",
desired_capabilities=desired_caps) #desired_capabilities=可不写
time.sleep(1)
#这里进行所需要的操作
#关闭app 关闭后需要释放资源
driver.close_app()
driver.quit()
亦或者:
#1.连接设备
#组装一个字典,注明平台,系统版本,名称
#从appium导入webderiver
from appium import webdriver
import time
#连接移动设备所必须的参数
desired_caps = {
"deviceName": "127.0.0.1:7555", #mumu是7555
"platformName": "Android",
"latformVersion": "6.0.1",
"appPackage": "com.android.settings",
"appActivity": ".Settings",
"automationName":"Uiautomator2",
"noReset":True,
"newCommandTimeout":6000,
"unicodeKeyboard":True,
"resetKeyboard":True
}
#看系统
#系统的版本
#要启动的app的名称(app的唯一标识:包名)
#要启动的app的界面
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",
desired_capabilities=desired_caps)
time.sleep(1)
#g关闭app 关闭后需要释放资源
driver.close_app()
driver.quit()
App的重置策略
默认:测试后停止并清除应用数据,不卸载apk
fullReset:在会话开始前测试后停止app,清除app数据并卸载apk
noReset:不要停止应用程序,不要清除应用数据,不要卸载apk
Uiautomatorviewer的使用
Uiautomatorviewer主要用于查看应用中的元素特征,帮我们在代码中进行元素定位
使用方法:
在android_sdk路径下进入
ADB进入命令行:
首先进入python模式
再下指令pip list查看当前已经安装的包
workon查看当前的虚拟环境。
从Remote点击查看webdriver.py
Appnium端需要配置 的环境变量:
退出configuraations后:
端口号,与代码端口号要一致。host的ip地址也要一致?
点击start:(运行代码前要start)出现以下说明安装没有问题
代码中的的信息如何查找:
1.deviceName:
desired_caps["deviceName"] = "172.0.0.1:62001"
2.看系统
desired_caps["platformName"] = "Android"
注意首字母大写
3.看系统版本
#系统的版本
desired_caps["latformVersion"] = "7.1"
模拟器中查看:
4.要启动的app名称
#要启动的app的名称(app的唯一标识:包名)
desired_caps["appPackage"] = "com.android.setting"
5.要启动的app界面
#要启动的app的界面
desired_caps["appActivity"] = ".Settings"
4和5使用命令在命令行中查看:
获取手机当前运行的程序和界面的名称:
adb shell dumpsys window windows | findetr mFocusedApp
3)测试原理
代码通过remote连接服务器(Appnium client),将信息传到server(appnium),需要解析,server通过adb命令传递指令给手机。
4)基础操作
基础API:
- driver
close_app() | 关闭打开的应用 |
quit() | 断开连接(后续不能发送指令了) |
install_app('apk在电脑的绝对路径') | 安装应用 |
remover_app('应用的包名') | 卸载应用 |
is_app_installed('应用的包名') | 判断应用是否安装 |
push_file(目标位置,base64编码的内容) | |
pull_file(来源位置) | 返回值是base64编码的内容 |
pase_source | 获取界面xml源码 |
find_element... | |
find_elements... | |
current_package | 获取当前操作的应用的包名 |
current_activity | 获取当前操作的界面名称 |
- element
text | 获取元素文本的内容 |
click() | 点击元素对应的位置 |
get_attribute(属性名称) | 获取属性值 |
location | 获取元素左上角的坐标(相对于屏幕的左上角) |
size | 获取元素的宽高(字典) |
el = driver.find_element_by_xpath("//*[@text='显示']")
print(el.size)
print(el.text)
print(el.get_attribute("text"))
print(el.location)
5)输入文本
对于有些元素,可以接受文本输入。
- send_keys(文本内容)
可以输入文字,但对中文,在连接设备的时候要加上配置。(现在默认是可以的输入中文,若是不可以则把以下两行内容进行配置)
desired_caps('unicodeKeyboard')= True #unicode设置(允许中文输入)
desired_caps['resetKeyboard'] = True #键盘设置(允许中文输入)
如果对于同一元素,多次调用此方法,会先一个一个删除掉原内容,再输入。
- clear()
清除文本框的内容。(最后一个不会自动删除,需要清楚一个)
eg:
driver.find_element_by_xpath("//*[@resource-id='com.android.setting:id/search']").click()
time.sleep(1)
driver.find_element_by_xpath("//*[@resoure id='android:id/search_src_text']").send_keys('abd')
driver.find_element_by_xpath("//*[@resoure-id='android:id/search_src_text']").send_keys('123')
driver.find_element_by_xpath("//*[@resoure-id='android:id/search_src_text']").send_keys('张三')
time.sleep(2)
driver.find_element_by_xpath("//*[@resoure-id='android:id/search_src_text']").clear()
6)元素定位——uiautomatorviewer的使用
与web自动化不一样,web元素是在html上,而移动端是在xml上,可通过print(driver.pase_source)指令查看到。定位元素使用xpath的方式。
uiautomatorviewer主要用于查看应用种的元素属性特征,帮助我们在代码中进行元素定位。
使用方法:
- 在android_sdk路径下进入D:\Software\android-sdk-windows\tools路径,直接双击uiautomatorviewer.bat即可启动。
- 电脑连接真机或模拟器。
- 启动待测试的app。
- 点击uiautomatorviewer左上角的Device screenshot按钮。
- 点击希望查看属性的元素。
- 查看右上角的node detail。
通过以下这个bat运行一个截图工具,查看页面的元素。
打开后,黑色的控制台页面也不要关闭。
点击打开:
进去之后,点击左上角UI字眼下面偏绿色的按钮进行截图。再通过滑动点击页面元素,可在右边查看到元素信息。
主要用到的内容
index指父元素下的第几个元素,不是唯一的,一般不使用
常见元素定位:
- id
id_value:元素的id属性值
注意在android中,id属性即resource_id
driver.find_element('id',id_value)
- 通过xpath定位
xpath_value:xpath表达式
在android中,toast必须通过xpath定位
driver.find_element('xpath',xpath_value)
一个计算器的测试
一般bounds不常用,不同的界面的分辨率不同,bounds就会不一样
from appium import webdriver
import time
#连接移动设备所必须的参数
desired_caps = {
"deviceName": "127.0.0.1:7555", #mumu是7555
"platformName": "Android",
"latformVersion": "6.0.1",
"appPackage": "com.huangxj.calculator",
"appActivity": "com.ynlh.jsq.Jsq",
"automationName":"Uiautomator2",
"noReset":True,
"newCommandTimeout":6000,
"unicodeKeyboard":True,
"resetKeyboard":True
}
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",
desired_capabilities=desired_caps)
time.sleep(1)
driver.find_element('id','com.huangxj.calculator:id/num4').click()
driver.find_element('id','com.huangxj.calculator:id/b1').click()
driver.find_element('id','com.huangxj.calculator:id/num5').click()
#driver.find_element('id','com.huangxj.calculator:id/result').click()
#使用text定位
driver.find_element('xpath','//*[@text="="]').click()
time.sleep(1)
#通过bounds定位,需要先定位到class,web中的写法:'//div[@class="xxxx"]'
print(driver.find_element('xpath', '//android.widget.EditText[@bounds="[0,46][773,176]"]').text)
#g关闭app 关闭后需要释放资源
driver.close_app()
driver.quit()
#从appium导入webderiver
from appium import webdriver
import time
#连接移动设备所必须的参数
desired_caps = {} # desired_capabilities 是字典,先定义一个空字典
#当前要测试的设备名称
desired_caps["deviceName"] = "127.0.0.1:7555"
#看系统
desired_caps["platformName"] = "Android"
#系统的版本
desired_caps["latformVersion"] = "7.1"
#要启动的app的名称(app的唯一标识:包名)
desired_caps["appPackage"] = "com.android.settings"
#要启动的app的界面
desired_caps["appActivity"] = ".Settings"
driver = webdriver.Remote("http://127.0.0.1:4444/wd/hub",desired_capabilities=desired_caps)
time.sleep(1)
print(driver.pase_source)
# //*[text()='显示'] 的意思是在整个页面下寻找任意的元素(*),限定条件text
#在安卓手机中,text并不是文本,而是属于标签的属性
driver.find_element('xpath',"//*[text()='显示']").click()
#g关闭app 关闭后需要释放资源
driver.close_app()
driver.quit()
以上报错原因是:< />,即开始和结束标签是在一起的,在安卓手机中,text并不是文本,而是属于标签的属性。
在xml中
<a herf="www.baidu.com">你好</a> 你好就是a标签的text,herf指的就是a标签的属性
如果中间没有值,可以直接如下写:
<a herf="www.baidu.com"/>
改成:
driver.find_element_by_xpath("//*[text()='显示']").click()
#改成:
driver.find_element_by_xpath("//*[@text()='显示']").click()
一般情况下,通过文本获取,少数情况下通过id。
常见错误
Error while obtaining UI hierarchy XML file:com.android.ddmilb.SyncException:Remote object doesn'y exist!
这个错误的原因是因为没有dump下来界面的信息保存到uidump.xml文件中,而该文件保存在/data/local/tmp下,如果没生成该文件,uiautomatorviewer就会报这个错。
解决方案:重启设备或者重新连接。
7)对混合app元素的识别
移动应用的三种常见类型:
- 原生应用(Native)
- 混合应用(Hybrid):原生+H5,应用的比较多
- 纯H5应用(browser)
混合应用eg:拼多多
appium对于混合应用测试的步骤:
- 安装uc-dectools,然后打开uc-devtools,查看webview中的界面结构和元素属性;
- 配置appium使用的chromedriver,主要用于驱动H5界面的测试操作,原理类似于web自动化测试;
- 在代码中获取所有context的名字,然后切换context到H5页面的context中,再进行相应的操作;
- 切换context到原生app中,继续进行其他操作。
安装后使用说明:
也可以使用电脑浏览器输入移动端网页的地址进行元素定位。
信息查看:
chromedriver各版本下载:
https://npm.taobao.org/mirrors/chromedriver/
下载对应chromedriver文件后将chromedriver.exe放
C:\Program Files (x86)\Appium\resources\app\node_modules\appium\node_modules\appium-chromedriver\chromedriver\win
或
C:\Program Files\Appium\resources\app\node_modules\appium\node_modules\appium-chromedriver\chromedriver\win
或
C:\Program Files\Appium Server GUI\resources\app\node_modules\appium\node_modules\appium-chromedriver\chromedriver\win
下即可(备份默认的)或者通过appium server指定chromedriver路径也可以,要带上exe。
若是网页中没有看到66的版本,则可以点击进去查看notes.txt
设置webdriver的存放路径:
一些操作键:
Appium移动自动化测试-----(十一)appium API 之键盘操作 - #甜甜8023 - 博客园
- pressKeyCode()
电话键
KEYCODE_CALL 拨号键 5
KEYCODE_ENDCALL 挂机键 6
KEYCODE_HOME 按键Home 3
KEYCODE_MENU 菜单键 82
KEYCODE_BACK 返回键 4
KEYCODE_SEARCH 搜索键 84
KEYCODE_CAMERA 拍照键 27
KEYCODE_FOCUS 拍照对焦键 80
KEYCODE_POWER 电源键 26
KEYCODE_NOTIFICATION 通知键 83
KEYCODE_MUTE 话筒静音键 91
KEYCODE_VOLUME_MUTE 扬声器静音键 164
KEYCODE_VOLUME_UP 音量增加键 24
KEYCODE_VOLUME_DOWN 音量减小键 25
控制键
KEYCODE_ENTER 回车键 66
KEYCODE_ESCAPE ESC键 111
KEYCODE_DPAD_CENTER 导航键 确定键 23
KEYCODE_DPAD_UP 导航键 向上 19
KEYCODE_DPAD_DOWN 导航键 向下 20
KEYCODE_DPAD_LEFT 导航键 向左 21
KEYCODE_DPAD_RIGHT 导航键 向右 22
KEYCODE_MOVE_HOME 光标移动到开始键 122
KEYCODE_MOVE_END 光标移动到末尾键 123
KEYCODE_PAGE_UP 向上翻页键 92
KEYCODE_PAGE_DOWN 向下翻页键 93
KEYCODE_DEL 退格键 67
KEYCODE_FORWARD_DEL 删除键 112
KEYCODE_INSERT 插入键 124
KEYCODE_TAB Tab键 61
KEYCODE_NUM_LOCK 小键盘锁 143
KEYCODE_CAPS_LOCK 大写锁定键 115
KEYCODE_BREAK Break/Pause键 121
KEYCODE_SCROLL_LOCK 滚动锁定键 116
KEYCODE_ZOOM_IN 放大键 168
KEYCODE_ZOOM_OUT 缩小键 169
组合键
KEYCODE_ALT_LEFT Alt+Left
KEYCODE_ALT_RIGHT Alt+Right
KEYCODE_CTRL_LEFT Control+Left
KEYCODE_CTRL_RIGHT Control+Right
KEYCODE_SHIFT_LEFT Shift+Left
KEYCODE_SHIFT_RIGHT Shift+Right
基本
KEYCODE_0 按键’0’ 7
KEYCODE_1 按键’1’ 8
KEYCODE_2 按键’2’ 9
KEYCODE_3 按键’3’ 10
KEYCODE_4 按键’4’ 11
KEYCODE_5 按键’5’ 12
KEYCODE_6 按键’6’ 13
KEYCODE_7 按键’7’ 14
KEYCODE_8 按键’8’ 15
KEYCODE_9 按键’9’ 16
KEYCODE_A 按键’A’ 29
KEYCODE_B 按键’B’ 30
KEYCODE_C 按键’C’ 31
KEYCODE_D 按键’D’ 32
KEYCODE_E 按键’E’ 33
KEYCODE_F 按键’F’ 34
KEYCODE_G 按键’G’ 35
KEYCODE_H 按键’H’ 36
KEYCODE_I 按键’I’ 37
KEYCODE_J 按键’J’ 38
KEYCODE_K 按键’K’ 39
KEYCODE_L 按键’L’ 40
KEYCODE_M 按键’M’ 41
KEYCODE_N 按键’N’ 42
KEYCODE_O 按键’O’ 43
KEYCODE_P 按键’P’ 44
KEYCODE_Q 按键’Q’ 45
KEYCODE_R 按键’R’ 46
KEYCODE_S 按键’S’ 47
KEYCODE_T 按键’T’ 48
KEYCODE_U 按键’U’ 49
KEYCODE_V 按键’V’ 50
KEYCODE_W 按键’W’ 51
KEYCODE_X 按键’X’ 52
KEYCODE_Y 按键’Y’ 53
KEYCODE_Z 按键’Z’ 54
例子:
拼多多
#1.连接设备
#组装一个字典,注明平台,系统版本,名称
#从appium导入webderiver
from appium import webdriver
#连接移动设备所必须的参数
desired_caps = {
"deviceName": "127.0.0.1:7555", #mumu是7555
"platformName": "Android",
"latformVersion": "6.0.1",
"appPackage": "com.android.browser",
"appActivity": ".BrowserActivity",
"automationName":"Uiautomator2",
"noReset":True,
"newCommandTimeout":6000,
"unicodeKeyboard":True,
"resetKeyboard":True
}
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",
desired_capabilities=desired_caps)
#隐式等待10s
driver.implicitly_wait(10)
driver.find_element('id','com.android.browser:id/url').clear()
driver.find_element('id','com.android.browser:id/url').click()
driver.find_element('id','com.android.browser:id/url').send_keys('https://mobile.yangkeduo.com/')
driver.press_keycode(66) #回车事件
context = driver.contexts #获取当前app中所有的上下文对象
print(context) #就是一个webview
#两个不同的context,需要切到h5对象中
#切换上写我到H5页面中
driver.switch_to.context('WEBVIEW_com.android.browser')
driver.find_element('xpath', "//span[text()='女装']").click()
#driver.find_element('xpath',"//*[@class='_1Hiz2ym5']").click()
# #g关闭app 关闭后需要释放资源
# driver.close_app()
# driver.quit()
两个不同的context,需要切到h5页面
8)练习
下列列表中又四个字典,每个字典中的num1代表第一个操作数,num2代表第二个操作数,cp代表运算符。
现要求用代码读取这个列表中的字典,在计算器apk中自动化完成对应的运算操作,并通过读取计算器的结果框中显示的数字,打印运算结果。
from appium import webdriver
desired_caps = {
'deviceName':'127.0.0.1:7555',
'platformName':'Android',
'latformVersion':'6.0.1',
'appPackage': 'com.ibox.calculators',
'appActivity': '.CalculatorActivity',
'automationName':'Uiautomator2',
'noReset':True,
'newCommandTimeout':6000,
'unicodeKeyboard':True,
'resetKeyboard':True
}
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
driver.implicitly_wait(10)
dic = [
{'num1':78,'num2':98,'op':'+'},
{'num1':56,'num2':34,'op':'-'},
{'num1':23,'num2':2,'op':'*'},
{'num1':123,'num2':89,'op':'/'}
]
op_dic={
'+':'plus',
'-':'minus',
'*':'mul',
'/':'div'
}
for exp in dic:
for word in str(exp['num1']):
driver.find_element('id','com.ibox.calculators:id/digit{}'.format(word)).click()
driver.find_element('id', 'com.ibox.calculators:id/{}'.format(op_dic[exp['op']])).click()
for word in str(exp['num2']):
driver.find_element('id','com.ibox.calculators:id/digit{}'.format(word)).click()
driver.find_element('id', 'com.ibox.calculators:id/equal').click()
cal = driver.find_element('xpath','//android.widget.TextView[@index="0"]').text
result = driver.find_element('xpath','//android.widget.TextView[@index="1"]').text
print(cal+result)
(四)模拟手势
1)安卓内在机制
- 对于目前不需要在屏幕中显示的内容,是不需要处理的,为了节省硬件资源;
- 安卓使用了栈的方式管理界面;
- 页面滑动有“惯性”机制,会根据按下、抬起的位置以及总的时间,滚动不一样的距离。
2)滚动/滑动
01 swipe——基于坐标的滑动
从一个坐标点滑动界面到另一个坐标点,通常用于开屏动画滑动、多图片连续滑动等
swipe(self,start_x:int,start_y:int,end_x:int,end_y:int,duration:int = 0)
简写:
driver.swipe(x1,y1,x2,y2,duration)
注意duration默认是600单位是毫秒,持续时间,时间越长,滑得越慢。
它会影响实际滚动的距离
还需注意:click方法,实际上并不是点元素,而是点元素所在位置。在获取的时候有位置,但可以经过滑动,位置发生了变化,可能点不准。非常有可能在点击之前sleep一会,等待滑动结束。
坐标也可以用uiautomatorviewer查看
driver.swipe(550,800,550,400,duration=5000)
02 scroll
从一个元素滑动到另外一个元素,知道页面自动停止,模拟人活动一下的操作。必循要求两个元素在页面上可见,否则会报错
driver.scroll(origin_el,destination_el,duration)
scroll直接传递元素作为参数即可,不需要手动获取位置
scroll底层实现:与swipe有一些区别,没有中间的采样点,只有起始、结束,但最终的效果和swipe是一样的。
滑动的时候慢一点,不然准确性不太好,可能达不到预期效果
orgin_el:滑动开始的元素
destination_el:滑动结束的元素
duration:滑动的持续时间,默认使600ms,时间越大滑动越慢。
from appium import webdriver
desired_cap = {
'deviceName':'127.0.0.1:7555',
'platformName':'Android',
'latformVersion':'6.0.1',
'appPackage':'com.android.settings',
'appActivity':'.Settings',
'automationName':'Uiautomator2',
'noRest':True,
'newCommandTimeout':6000,
'unicodeKeyboard':True,
'resetKeyboard':True
}
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_cap)
driver.implicitly_wait(10)
el1 = driver.find_element('xpath','//*[@text="应用"]')
el2 = driver.find_element('xpath','//*[@text="蓝牙"]')
#滑动的时候慢一点,不然准确性不太好,可能达不到预期效果
driver.scroll(el1,el2,duration=5000)
03 drag_and_drop事件
从一个元素滑动到另一个元素,第二个元素替代第一个元素原表在屏幕上的位置。也是两个元素必须在界面上可见,否则报错。
driver.drag_adn_drop(orgin_el,destination_el)
注意,drag_and_drop不能设置持续事件,但滑动效果比scroll更加精确,几乎没有惯性。
el1 = driver.find_element('xpath','//*[@text="应用"]')
el2 = driver.find_element('xpath','//*[@text="蓝牙"]')
driver.drag_and_drop(el1,el2)
例子
eg:
预估位置:
driver.swip(start_x=270,start_y=640,end_x=270,end_y=320)
第二种方式:
size = driver.get_window_size()
prinnt(size)
width = size["width"]
height = size["height"]
driver.swipe(start_x=width/2,start_y=height/3*2,
end_x=width/2,end_y=height/3)
滚动:
el1=driver.find_element('xpath',"//*[@text='通知']")
el2=driver.find_element('xpath',"//*[@text='WLAN']")
driver.scoll(el1,el2)
关于坐标:
第二种方式的滑动print:
3)拖拽
在安卓中,拖拽=按下 等待一定时间 移动 松手
drag_and_drop(origin_el,destinatiion_el)
eg:
el1.driver.find_element('xpath',"//*[@text='酷安']")
el2.driver.find_element('xpath',"//*[@text='京东']")
driver.drag_and_drop(el1,el2)
time.sleep(2)
4)TouchAction
- 作用
构建相对比较复杂的、连续的触摸动作
- 用法
- 创建TouchAction对象,需要把driver作为参数传递;
- 通过各种方法添加动作/通过对调用想执行的手势;
- 执行操作perform()方法。(所有手势必须通过perform方法来触发)
- 方法
- 按下
press(self,el,x,y,pressure)
如果传递了el参数,x,y可以不传
如果el为none,就需要传递x,y
pressure是ios专用的
- 长按
long_press(self,el,x,y,duration=1000)
如果传递了el参数,x,y可以不传
如果el为none,就需要传递x,y
#长按 long_press
el1 = driver.find_element('xpath','//*[@text="WLAN"]')
ta = TouchAction(driver) #构造一个touchaction对象
ta.tap(el1).perform()
#太快可能长按没有效果
time.sleep(2)
ta.long_press(x=131,y=264,duration=1000).perform()
- 移动
move_to(self,el,x,y)
如果传递了el参数,x,y可以不传
如果el为none,就需要传递x,y
- 等待
wait(self,ms)
ms等待时间,单位毫秒,默认600ms
- 松手
release()
- 轻敲
TouchAction(driver).tap(element=None,x=None,y=None,count=1)
tap和click的区别:click有延迟促发效果(为了校验是不是双击),通过tap(el,count=2)可以模拟双击。方法作用差不多,但tap可以接收一个坐标值作为敲击区域,而click只能接收对象作为参数。
from appium import webdriver
from appium.webdriver.common.touch_action import TouchAction
desired_caps = {
"deviceName": "172.0.0;1:7555", #mumu是7555
"platformName": "Android",
"latformVersion": "6.0.1",
"appPackage": "com.android.settings",
"appActivity": ".Settings",
"automationName":"Uiautomator2",
"noReset":True,
"newCommandTimeout":6000,
"unicodeKeyboard":True,
"resetKeyboard":True
}
driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)
driver.implicitly_wait(10)
#敲击 tap
el1 = driver.find_element('xpath','//*[@text="更多"]')
ta = TouchAction(driver) #构造一个touchaction对象
#通过元素
# ta.tap(el1).perform()
#通过坐标
ta.tap(x=600,y=500).perform()
现在已弃用,需使用W3C actions。
实例:
#需要再导一个包,其他内容要包括基本内容
from appnium.webdriver.common.touch_action import TouchAction
el1.driver.find_element_by_xpath("//*[@text='通知']")
el2.driver.find_element_by_xpath("//*[@text='WLAN']")
#实例化TouchAction
action = TouchAction(driver)
#既可以使用坐标又可以使用元素
action.press(el1).wait(2000).release()
#分开写:
#在移动的过程中wait必不可少,wait(0)是默认600ms
action.press(el1).wait(2000).move_to(el2)
#x和y是绝对坐标
action.press(x=270,y=640).wait(500).move_to(x=270,y=370)
action.release()
#执行 模拟收拾的使用,TouchActio进行模拟手势的时候,一定要记得执行操作
action.perform()
time.sleep(3)
5)图形滑动案例
案例如下:
安全不显示在当前屏幕中,需要滑动到下面让其显示在屏幕前,才能查找到
import time
from appium import webdriver
from appium.webdriver.common.touch_action import TouchAction
desired_caps = {
"deviceName": "172.0.0;1:7555", #mumu是7555
"platformName": "Android",
"latformVersion": "6.0.1",
"appPackage": "com.android.settings",
"appActivity": ".Settings",
# 'appActivity':'.ChooseLockPattern',这样可以直接打开对应activity
"automationName":"Uiautomator2",
"noReset":True,
"newCommandTimeout":6000,
"unicodeKeyboard":True,
"resetKeyboard":True
}
driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)
driver.implicitly_wait(10)
#移动move to,首先按住第一个点,用press,最好需要release方法释放手指
el1=driver.find_element('xpath',"//*[@text='内存']")
el2=driver.find_element('xpath',"//*[@text='WLAN']")
driver.drag_and_drop(el1,el2)
time.sleep(1)
driver.find_element('xpath',"//*[@text='安全']").click()
time.sleep(1)
driver.find_element('xpath',"//*[@text='屏幕锁定方式']").click()
time.sleep(1)
driver.find_element('xpath',"//*[@text='图案']").click()
time.sleep(1)
action = TouchAction(driver)
#按下的位置 Z型,可以不wait,也可以单独分开move_to,但每次都要perform()
action.press(x=125,y=512).wait(200).move_to(x=386,y=512).wait(200).move_to(x=648,y=512).wait(200).move_to(x=386,y=778).wait(200).move_to(x=128,y=1038).wait(200).move_to(x=647,y=1038)
action.release()
action.perform()
6)基础操作
- device_time :获取手机时间
- get_window_size():获取屏幕大小
- network_connection :获取手机网络信息
- 飞行模式
- WiFi
- 移动数据 6=2+4
- set_network_connection(网络模式数字) :设置手机网络信息
- keyevent(按键数字) :点击按键
- set_screenshot_as_file/save_screenshot(图片路径) :截屏
- open_notification() :打开通知栏
eg:
print(driver.device_time)
print(driver.network_connection)
driver.get_screenshot_as_file("jietu.png")
7)appium其他操作手机操作
- 能够使用appium在脚本内启动其他app
- 能够使用appium获取包名和activity的名字
- 能够使用appium关闭app和驱动对象
- 能够使用appium安装和卸载app
- 能够使用appium将应用置于后台
- 获取手机分辨率
- 手机截图
- 获取和设置网络状态
- 发送按键事件到设备
- 操作手机通知栏
1、能够使用appium在脚本内启动其他app
driver.start_activity(appPackage,appActivity) #被启动的包和activity名
2、能够使用appium获取包名和activity的名字
driver.current_activity
driver.current_package
3、能够使用appium关闭app和驱动driver对象
driver.close_app #关闭当前app
driver.quit() #退出driver对象
4、能够使用appium安装和卸载app
driver.install_app(app_path) #安装
driver.remove_app(app_id) #app_id应用程序的包名 卸载
#返回值为bool值,True为已安装,False为未安装
driver.is_app_install(app_id)
5、能够使用appium将应用置于后台
模拟home键,将应用置于后台。
driver.background_app(seconds) #后台停留时间/秒,停留秒数打倒后,应用会自动回到前台
某些应用在进入后台一段时间后,重新回来前提时会要求输入密码,如果自动化需要测试这种功能,可以使用这个api来测试。
涉及两个概念:
热启动:应用从后台回到前台。
冷启动:第一次打开某个应用。
6、获取手机分辨率
driver.get_window_size()
7、手机截图
driver.get_screenshot_as_file(filename)
8、获取和设置网络状态
#获取手机网络状态(返回网络状态编码,具体数字含义如下)
driver.network_connection
#设置手机网络状态
#connectionType:网络状态码
driver.set_network_connection(connectionType)
value(Alias) | Data | Wifi | Airplane Mode |
0(None) | 0 | 0 | 0 |
1(Airplane Mode ) | 0 | 0 | 1 |
2(Wifi only) | 0 | 1 | 0 |
4(Data only) | 1 | 0 | 0 |
5(All network on) | 1 | 1 | 0 |
9、发送按键事件到设备
发送按键到设备就是模拟发送home键、返回键等操作,比如按两次返回键退出应用。
#发送按键的代码
driver.press_keycode(keycode)
常用按键编码:Android KeyCode列表 - petercao - 博客园
10、操作手机通知栏
测试某些消息推送应用时,需要检测通知栏是否有消息通知
#打开通知栏
driver.open_notifications()
8)练习
1、用appium打开settings的页面,并完成点击“开发者选项”的click操作.
需要考虑屏幕分辨率和屏幕大小,不是所有手机的这 两个元素都在页面上,需要考虑自适应。
利用异常处理,可以循环判断,如果异常了,就往下滚一下。
from appium import webdriver
from selenium.common.exceptions import NoSuchElementException
desired_caps = {
'deviceName':'127.0.0.1:7555',
'platformName':'Android',
'latformVersion':'6.0.1',
'appPackage':'com.android.settings',
'appActivity':'.Settings',
'automationName':'Uiautomator2',
'noReset':True,
'newCommandTimeout':6000,
'unicodeKeyboard':True,
'resetKeyboard':True
}
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
#driver.implicitly_wait(10)
# el1 = driver.find_element('xpath','//*[@text = "内存"]')
# el2 = driver.find_element('xpath','//*[@text = "蓝牙"]')
# driver.drag_and_drop(el1,el2)
# driver.find_element('xpath', '//*[@text = "开发者选项"]').click()
while True:
try:
driver.find_element('xpath', '//*[@text = "开发者选项"]').click()
break
except NoSuchElementException:
print('没有找到元素')
driver.swipe(535, 670, 535, 400, duration=500)
2、针对马上记账app中的日期选择界面,封装一个能够选择任意日期的函数select_date,这个函数接受年月两个个参数,appium会根据用户输入的参数值选择对应的日期。
日期控件常见的处理方式:
1.如果是第三方库,并且又公开的api接口(问开发),用python或Android的uiautomator工具去直接调用接口,从而设置日期的值,避免从界面上操作,效率非常高
2.通过界面方式进行滑动选择操作,这种方式比较通用,没有api也可以直接使用,的那比较麻烦,编程和操作效率比较低。
from appium import webdriver
desired_caps={
'deviceName':'127.0.0.1:7555',
'platformName':'Android',
'latformVersion':'6.0.1',
'appPackage':'com.laogejizhang.account',
'appActivity':'.MainActivity',
'noReset':True,
'newCommandTimout':6000
}
def select_date(year,month):
while True:
current_year = driver.find_element('xpath', '//*[@bounds="[244,1199][374,1233]"]').text
print(current_year)
if current_year == year:
break
if int(current_year) > int(year):
driver.swipe(274, 1174, 274, 1213, duration=1000)
else:
driver.swipe(274, 1213, 274, 1174, duration=1000)
while True:
current_mouth = driver.find_element('xpath', '//*[@bounds="[406,1199][536,1233]"]').text
print(current_mouth)
if current_mouth == month:
break
if int(current_mouth) > int(month):
driver.swipe(499, 1174, 499, 1213, duration=1000)
else:
driver.swipe(499, 1213, 499, 1174, duration=1000)
driver.find_element('xpath','//*[@text="确定"]').click()
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
driver.implicitly_wait(10)
# driver.find_element('xpath','//android.widget.Button[@index="1"]').click()
driver.find_element('xpath','//*[@text="流水"]').click()
# driver.find_element('xpath','//android.view.ViewGroup[@index="0"]/android.widget.TextView').click()
driver.find_element('xpath','//android.widget.TextView[@bounds="[299,49][451,87]"]').click()
select_date('2019','11')
报错解决
1、appium运行脚本提示Could not sign with default certificate.
使用管理员打开appium即可。
2、Original error: Cannot start the 'com.android.setting' application
包名写错
3、AttributeError: ‘dict‘ object has no attribute ‘click‘
遇到这个问题,百度的时候大多数都是find_element写成了find_elements
那么不是以上情况,就可能使appium的版本问题,需要更新下。
4、[Deprecated] 'TouchAction' action is deprecated. Please use W3C actions instead.
[已弃用] 'TouchAction' 操作已弃用。 请改用 W3C 操作。
参考:精心筹备半年 :appium2.0+ 单点触控和多点触控新的解决方案_appium1.0和2.0-CSDN博客
学习W3C action:Appium-W3C Action(W3C动作)_appium w3c-CSDN博客
W3C actions文档:https://www.kancloud.cn/testerhome/appium_docs_cn/2001822