目录
- 环境搭建
- ADB调试工具
- adb构成
- adb工作原理
- adb常用命令
- 电脑连接多个设备跟模拟器使用adb
- 包名与界面名的概念
- 如何获取包名和界面名
- 文件传输
- 获取app启动时间
- 获取手机日志
- 其他命令
- Appium全自动化测试框架(python)冲错了
- 序言
- 环境搭建
- Appium客户端安装
- Appium-python库安装
- Appium自动化测试框架(Java) 猛猛冲
- 序言
- 环境搭建
- Appium desktop安装
- JDK安装与配置(略)
- IDEA 安装(略)
- 模拟器安装(略)
- Android SDK安装与配置
- Appium desktop使用详解
- **Start Inspector Session 放大镜功能详解**
- Appium Inspector App元素探测工具
- UIAutomatorViewer (SDK自带原生元素定位工具)
- Appium自动化_前奏
- App类型
- App布局和控件
- Android Debug Birdeg (ADB命令)
- Maven
- ==HelloWorld_appium==
- Appuim初始化动作分析 (p20、p21)
- Appium常见元素定位
- Appium定位 经验总结
- resource-id定位
- text定位
- className定位
- xpath定位
- accessibility定位
- 坐标定位
- Appium元素等待
- 简单的总结
- 强制等待、隐式等待、显式等待 详解
- 隐式等待与显示等待结合使用(总等待时间的计算)
- 显示等待- 简便工具类
- Appium-java API详解
- ==报错调试圣经==
- Appium手势操作
- 手势 - 单点滑动
- 手势 - 多点触控
- Appium常用API
- 页面跳转
- 获取当前页面dom结构,获取当前页面类名
- 重置应用数据、判断App是否安装
- 向系统发送键值事件
- 截图功能
- 获取设备时间、设备DPI、Automationname、横竖屏状态
- Appium特殊元素 - toast
- 待发掘的模块
- Hybrid APP(混合应用)自动化
- HybridAPP的定义
- uc-devtools下载与安装 (webUI调试工具)
- 线上App开启webview调试(root)
- 线上App开启webview调试(非root)
- Hybrid应用自动化脚本
- 上下文context切换失败问题
- 遗留问题(埋坑)
1、Android 环境配置,JDK+SDK+Android 模拟器配置
2、ADB调试工具;
3、Appium环境搭建
4、第一个HelloWorld程序;
5、Appium基础操作API
6、UIAutomatorView
7、元素定位操作API
8、元素等待
9、元素操作API
10、滑动和拖拽事件
11、高级手势TouchAction
12、手机操作API
环境搭建
JAVA 环境
AndroidASDK 环境
Android SDK 下载地址: https://www.androiddevtools.cn/
Android SDK Platform Tools 下载地址: https://developer.android.google.cn/studio/releases/platform-tools
Appium-Desktop 下载地址: https://github.com/appium/appium-desktop/releases
Genymotion 下载地址: https://www.genymotion.com/download/
https://developer.android.com/tools
ADB调试工具
adb构成
- client端,在电脑上,负责发送adb命令
- daemon守护进程,在手机上,负责接收和执行adb命令
- server端,在电脑上,负责管理client和daemon之间的通信
adb工作原理
- client端将命令发送给server端
- server端会将命令发送给daemon端
- daemon端进行执行
- 将执行结果,返回给server端
- server端将结果再返回给client端
adb常用命令
查看设备连接情况
1. adb devices
==========
C:\Users\v_fsaifang>adb devices
List of devices attached
9889d5385354313342 device
==========
2. 抓日志
adb logcat -v time>C:\Users\v_fsaifang\Desktop\temp\a.txt
3. 获取包名和界面名
(Mac/Linux)
adb she1l dumpsys window windows | grep mFocusedApp
(windows)
adb shell dumpsys window windows | findstr mFocusedApp
4.
电脑连接多个设备跟模拟器使用adb
1. adb devices 查看设备连接情况
2. adb -s 设备连接编号名 接adc动作指令
实例
C:\Users\v_fsaifang>adb devices
List of devices attached
emulator-5554 device
9889d5385354313342 device
C:\Users\v_fsaifang>adb -s 9889d5385354313342 shell dumpsys window windows | findstr mFocusedApp
mFocusedApp=AppWindowToken{357f881 token=Token{2841c68 ActivityRecord{7d2538b u0 com.android.settings/.Settings$ConnectionsSettingsActivity t937}}}
C:\Users\v_fsaifang>adb -s 9889d5385354313342 shell am start -W com.android.settings/.Settings$ConnectionsSettingsActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.android.settings/.Settings }
Status: ok
Activity: com.android.settings/.Settings
ThisTime: 523
TotalTime: 523
WaitTime: 556
Complete
包名与界面名的概念
- 包名(Package)
包名是决定程序唯一性的身份ID(不是应用的名字),它对应着应用程序。 - 界面名(activity)
界面名,也可以叫做启动名,对应着应用程序的某个界面。
如何获取包名和界面名
Mac/Linux
adb she1l dumpsys window | grep mFocusedApp
windows
adb shell dumpsys window | findstr mFocusedApp
应用场景:在指定打开预期应用的预期界面是会用到。
举例:
模拟器已被adb成功识别并连接,已打开安卓应用,再获取当前应用界面的包名和界面用。
C:\Users\v_fsaifang>adb devices
adb server is out of date. killing...
* daemon started successfully *
List of devices attached
emulator-5554 device
C:\Users\v_fsaifang>adb shell dumpsys window | findstr mFocusedApp
mFocusedApp=Token{fd6de03 ActivityRecord{434feb2 u0 com.android.settings/.Settings t3}}
mFocusedApp=AppWindowToken{f73a180 token=Token{fd6de03 ActivityRecord{434feb2 u0 com.android.settings/.Settings t3}}}
C:\Users\v_fsaifang>adb shell dumpsys window | findstr mFocusedApp
mFocusedApp=Token{ffdb429 ActivityRecord{f925db0 u0 com.android.gallery3d/.app.GalleryActivity t4}}
mFocusedApp=AppWindowToken{ca063ae token=Token{ffdb429 ActivityRecord{f925db0 u0 com.android.gallery3d/.app.GalleryActivity t4}}}
C:\Users\v_fsaifang>adb shell dumpsys window | findstr mFocusedApp
mFocusedApp=Token{1b55a2a ActivityRecord{722c715 u0 com.android.settings/.Settings$PowerUsageSummaryActivity t3}}
mFocusedApp=AppWindowToken{22c601b token=Token{1b55a2a ActivityRecord{722c715 u0 com.android.settings/.Settings$PowerUsageSummaryActivity t3}}}
包名为:com.android.settings
com.android.gallery3d
界面名为:.Settings
.app.GalleryActivity
.Settings$PowerUsageSummaryActivity
文件传输
发送文件到手机
应用场景
将手机需要的数据(数据库文件)在电脑上调整好,直接发送给手机;
命令各式
adb push 电脑的文件路径 手机的文件路径
实例
将桌面的Test.txt发送到手机的sd卡的fangyizeTest目录下
adb push C:\Users\saifang\Desktop\temp\Test.txt /sdcard/fangyizeTest/
从手机拉取文件
应用场景
将手机产生的文件(数据库文件,日志文件)拉取到电脑中
命令格式
adb pull 手机文件路径 电脑文件路径
实例
将手机的sd卡的Test.txt拉取到桌面的temp目录下并命名为AAA.txt
adb pull /sdcard/fangyizeTest/Test.txt C:\Users\v_fsaifang\Desktop\temp\AAA.txt
获取app启动时间
应用场景
1.企业对应用程序的启动速度有要求,可使用该命令进行测试
2.即使企业对程序启动时间没有明确要求,可参照同类软件启动时间,启动时间没有超过一倍即可。
命令格式
adb shell am start -w 包名/启动名
实例
模拟器 adb shell am start -W com.cyanogenmod.filemanager/.activities.NavigationActivity
============================================================================
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.cyanogenmod.filemanager/.activities.NavigationActivity }
Status: ok
Activity: com.cyanogenmod.filemanager/.activities.NavigationActivity
TotalTime: 364
WaitTime: 365
Complete
============================================================================
安卓手机 adb shell am start -W com.android.settings/.Settings$ConnectionsSettingsActivity
============================================================================
C:\Users\v_fsaifang>adb -s 9889d5385354313342 shell am start -W com.android.settings/.Settings$ConnectionsSettingsActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.android.settings/.Settings }
Status: ok
Activity: com.android.settings/.Settings
ThisTime: 523
TotalTime: 523
WaitTime: 556
Complete
============================================================================
以上,可以看出模拟器的adb输出没有ThisTime一项
ThisTime: activity启动耗时(毫秒)
TotalTime: 应用层启动耗时=thistime+应用application等资源启动时间(毫秒)
WaitTime: 系统启动应用耗时=totaltime+系统资源启动时间(毫秒)
获取手机日志
如何获取手机日志
adb logcat -v time>C:\Users\fang\Desktop\temp\a.txt
使用步骤
1.打开需要测试的应用程序
2.找到触发bug的位置
3.使用查看日志命令
4.触发bug
5.获取日志信息
示例
1.安装bug.apk 【可以使用Android语言自己开发一个】
2.打开《有bug的程序》应用程序
3.命令行中输入adb logcat命令
4.点击登录按钮
5.获取日志信息
应用场景
当程序发生崩溃的时候,可以将日志信息发送给开发人员,便于其快速的定位bug
关于崩溃的处理,需要找日志中的“at”前面,的第一个字符是E的就是错误信息,可以记在记事本发给开发。
其他命令
adb install 路径/xx.apk 安装app到手机
adb uninstall 包名 卸载手机上的app,需要指定包名
adb devices 获取当前电脑已经连接设备和对应的设备号
adb shell 进入到安卓手机内部的linux系统命令行中. 是的,Linux命令!直接无敌! exit退出哦
adb start-server 启动adb服务端
adb kill-server 停止adb服务端
adb --help 查看adp帮助
Appium全自动化测试框架(python)冲错了
序言
Appium介绍
Appium是一个移动端的自动化框架,可用于测试原生应用,移动网页应用和混合型应用,且是跨平台
的。可用于iOS和Android操作系统。原生的应用是指用android或iOS的sdk编写的应用,移动网页应用
是指网页应用,类似于ios中safari应用或者Chrome应用或者类浏览器的应用。混合应用是指一种包裹
webview的应用,原生应用于网页内容交互性的应用。 重要的是Appium是跨平台的,何为跨平台,意思
就是可以针对不同的平台用一套api来编写测试脚本。
Appium自动化测试环境搭建
我们使用Appium和python来进行自动化测试,需要安装两个东西,一个是Appium的客户端,一个是
Appium-python库。这两个需要安装的东西在加上手机就可以进行自动化测试,它们之间的关系是:
python代码 -> Appium-python库 -> Appium -> 手机。
环境搭建
Appium客户端安装
Appium-Desktop 下载地址: https://github.com/appium/appium-desktop/releases
此文章采用的是appium-desktop-setup-1.8.2.exe
Appium-python库安装
CMD:pip install Appium-Python-Client
CMD: pip list
Package Version
-------------------- ---------
Appium-Python-Client 2.11.1 出现就是安装成功
可能出现问题1:
WARNING: You are using pip version 22.0.4; however, version 23.2.1 is available.
You should consider upgrading via the 'C:\Users\v_fsaifang\Desktop\JAVA\QA\python\python.exe -m pip install --upgrade pip' command.
那就粘贴‘’里的命令升级就好了,直接解决
C:\Users\v_fsaifang>C:\Users\v_fsaifang\Desktop\JAVA\QA\python\python.exe -m pip install --upgrade pip
Requirement already satisfied: pip in c:\users\v_fsaifang\desktop\java\qa\python\lib\site-packages (22.0.4)
Collecting pip
Downloading pip-23.2.1-py3-none-any.whl (2.1 MB)
---------------------------------------- 2.1/2.1 MB 33.5 MB/s eta 0:00:00
Installing collected packages: pip
Attempting uninstall: pip
Found existing installation: pip 22.0.4
Uninstalling pip-22.0.4:
Successfully uninstalled pip-22.0.4
Successfully installed pip-23.2.1
可能出现的问题2:
错误提示:could not create '/usr/local/lib/python2.7/dist-packages/virtualenv_support':
Permission denied
权限问题,使用管理员运行cmd
可能出现的问题3:
pip不是批处理命令,那就是python开发环境没搭好,见下跟着搭
前提条件是python的开发环境已经搭建好了。
https://www.python.org/
Downloads
找一个想要的版本,根据自己的系统类型跟位数选择
Customize installation 自定义安装
勾选Add Python 3.6 to PATH (将python配置进环境变量)
勾选Install for all user 选择安装目录
完成安装后,cmd(重新打开一个) python,出现版本号安装成功并成功配入环境变量
pycharm
- 打开手机模拟器
- 打开appium工具
- 创建一个python项目,取名为 hello_appium
- 创建一个 demo.py 文件
- 代码运行
from appium import webdriver
desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
driver.quit()
Appium自动化测试框架(Java) 猛猛冲
序言
Appium官网地址: https://appium.io/
特点:
- 跨架构,支持原生、混合以及web移动应用
- 跨平台,Android & lOS
Appium组件:
- Appium Server (Appium的核心,实质上是一个web接口服务器,使用node.js实现
- Appium GUI (Appium Desktop的前身,已停止维护,封装的Appium Server是1.4.16
- Appium Desktop(推荐)(内嵌了Appium Server,提供了GUI界面,还包括其他工具的整合(如Appium Inspector)
- Appium Client (客户端向Appium Server发起请求,可以用不同语言来实现,如
https://github.com/appium/java-client
相关博客文章
https://www.cnblogs.com/yyoba/category/1247322.html
App自动化测试教程大纲
一.
App自动化测试概述
课程背景
课程学习
主流App测试框架对比
二.
Appium基础
环境部署概述
Appium-desktop安装
模拟器安装
Android SDK环境配置
Appium-desktop基本配置说明
aapt工具使用
Appiurm inspector工其使用
三.
App前端基础
App类型简介
UIAutomatorViewer元系定位工具App页向阻嘘
App常用布局
App常用控件
adb命令操作
包名&类名
四.
Appium进阶
Capalbilities配置介绍
Maven项目配置、创建
第一个App自动化脚本
用例脚木编写,testng组织用例
Appium自动化原理解析
Appiurm初始化日志分析
Appiurm元素定位-id、text. xpath....
Appium手势操作-滑动、多次滑动
Appiurn于势操怍-多点触摸
UI元素等待
Appium常用API
Toast信息获取
Hybrid应用白动化
webview debug开关
环境搭建
Appium desktop安装
Appium官方环境搭建步骤较为繁琐,初学者可使用appium-desktop
https://github.com/appium/appium-desktop/releases/tag/v1.22.3-4
Appium-desktop 装好后,如下图:
JDK安装与配置(略)
这里使用的是JDK8
IDEA 安装(略)
JAVA编译器
模拟器安装(略)
这里使用的是雷电模拟器,(用于模拟安卓设备)
Android SDK安装与配置
安卓开发组件
资源下载地址: https://www.androiddevtools.cn/
android-sdk_r24.4.1-windows.zip解压后,如下图
点击启动安装SDK Manager.exe,选择相应的工具包进行下载
对于扩展选项:这里建议全选
点击下载
Accept License 全部接受许可
都勾选后,点击Install下载 。
小知识:
理论上来说,extras中的东西如果网速允许,时间充沛的话,就都下载了吧,应为都是好东西。不过一开始安装的话,可以只用安装上图中的三个,即Android Support Repository、Android SupportLibrary和Google USB Driver。其他的以后有时间再慢慢下载吧。要注意,由于这些东西都是在google 的服务器上下载的。由于俺们天朝有墙,所以可能会出现连接不上的情况。
这种时候,我们可以通过有Android SDK的国内镜像服务器来下载安装,这里推荐几个:
1、中科院开源协会镜像站地址:
IPV4/IPV6 : http://mirrors.opencas.ac.cn 端口:80
2、北京化工大学镜像服务器地址:
IPv4: http://ubuntu.buct.edu.cn/ 端口:80
IPv4: http://ubuntu.buct.cn/ 端口:80
IPv6: http://ubuntu.buct6.edu.cn/ 端口:80
3、大连东软信息学院镜像服务器地址:
http://mirrors.neusoft.edu.cn 端口:80
点击菜单中的“Tools”,然后选择下拉中的“Options…”然后在弹出的对话框中,填写HTTP Proxy Server为mirrors.neusoft.edu.cn(镜像服务器的地址,注意前面不要加http),然后填写HTTP Proxy Port为80 (端口号)。最后在勾选下面的『Forcehttps://… sources to be fetched using http://…』复选框,如下图所示
具体可查看https://blog.csdn.net/Rainy_X/
https://blog.csdn.net/qq_46687516/
小知识截止线----------------------------------------------------------------------------------------------------------------------------------------------------------------------
注意:下载可能比较慢,耗费时间长,最重要的是保证网络好,没下载成功,就重进重选重新下载。
下载完成:
设置系统环境变量
系统变量-新建
Path-编辑
在Path路径中编辑添加三条安装路径;
1)添加 bulid-tools安装路径(在SDK安装目录下)
2)添加 tools安装路径(在SDK安装目录下)
3)添加platform-tools 安装路径(在SDK安装目录下)
win+r输入adb version,显示版本,安装成功。
关于ADB环境变量这里,有一个坑:
坑一:《adb被覆盖识别》
在我配置AndroidSDK的环境之前,我是直接将简化的ADB程序扔进了,C:\Users\v_fsaifang\中(用户环境中),导致我随后在系统变量里配置AndroidSDK后,也是优先识别用户环境下的ADB,导致系统环境里的AndroidSDK没有被识别。
解决方案一,干掉用户文件夹中的adb程序,也就是delete以上四个文件(或者新建个文件夹把他们包起来)。
解决方案二,提高环境变量path中AndroidSDK的那三个路径的优先级(直接把他们仨的位置移到最顶上)就可以被优先识别了。
关于模拟器与SDK冲突的坑
坑二:《模拟器自带adb与环境配置中的SDK版本不一致导致冲突》
如果你使用的是夜神模拟器,右键夜神模拟器点击“打开文件所在位置”,有一个nox_adb.exe程序文件会与SDK里的adb.exe冲突,可以将SDK里的adb.exe复制一份,并重命名为nox_adb.exe将夜神里的那个替换,解决该冲突。
Appium desktop使用详解
《基本配置介绍》
simple配置 (简单选项卡)
advanced配置 (高级选项卡)
presets (预先调整项)
以下是Appium运行界:
Start Inspector Session 放大镜功能详解
查看设备列表
adb decives
查看当前设备包名、页面信息
adb shell dumpsys window | findstr mFocusedApp
查看apk包信息
C:\Users\v_fsaifang\Desktop\JAVA\QA\android-sdk-windows\build-tools\29.0.3>aapt dump badging C:\Users\fang\Desktop\lolm_dev_cn_release_4.4.0.7102_64bit.signed.shell.apk
deviceName 设备名 emulator-5554
appPackage 包名 com.tencent.mm
appActivity app启动入口 com.tencent.mm.ui.LauncherUI
platformName 设备系统 Android
platformVersion 设备系统版本 7.1.2
unicodeKeyboard unicode输入键盘(appium自动化用执行涉及数据输入时,程序调用键盘输入) True
resetKeyboard 重置输入法 True
注意:下图中的,配置第二项测试平台名有误,应为platformName(多了个e)
大坑一
《找不到设备异常报错》
An unknown server-side error occurred while processing the command. Original error: Could not find a connected Android device.(处理命令时发生了未知的服务器端错误。原始错误:无法找到连接的Android设备。)
在我配置好环境变量,跟appuim的应用启动信息后,持续报这种错,试了很多种方法,在第六种解决:
一、 Appium-windows-1.18.2与android-sdk-windows版本不兼容。 查证资料得知,android-sdk用最新版就行,可以兼容之前的组件,至于Appium也是直接卸载换了最新的版本。
二、 Appium desktop应用启动配置信息错误。 确实有错,platformName这项原本多写了个e,以及后续查其他文章配置完善了其他未配置项;报错依旧,报错信息未变。
三、 设备未连接成功。 adb devices,显示设备在线,不是连接错误。
四、 模拟器自带内置adb版本与环境SDK冲突。 雷电adb.exe、夜神nox_adb.exe版本都替换了, 仍无效果,关键是真正的安卓设备也是一样的报错信息,错误源不对。
五、 测试的apk包没有安装至测试设备。 已安装,报错一致。
六、 这个简直是纯老六,最开始的环境变量配的是,系统环境变量ANDROID-SDK-HOME换行配路径,以及Path中的3个C:\\组件路径(按视频里那个老六配置的),结果他喵的居然可以在cmd识别出来,后来我才发觉这样配是有问题的,于是我重新配了一套环境变量,ANDROID_HOEM换行配路径,Path%ANDROID_HOME%组件,这时桌面的apk文件图标都变成了安卓小人(这时我就有成功的预感了),然后再启动appuim,就发现报错信息变了。ahhahaah 非常开心,因为报错信息显示的不是找不到设备,而是java的JDK了,说来也奇怪androidsdk环境配置错了居然报找不到设备的异常,可怜的代码;回归到新的报错信息上,先是java -version识别出了jdk版本号,说明Javajdk是没问题的,于是来到了appuim的初始页面(注意是未启动状态),Edit Configurations,就发现这个吊毛appuim自动识别的javajdk路径是错误的,于是我们手动输入正确的路径,及得保存,然后成功启动。
方法六,jdk路径配置截图:
让我们看看人工智障の标准回答:enmmm,我当时看了感觉就是一坨shit!
这个错误通常表示您正在尝试在Android开发环境中执行某个命令时,但没有找到已连接的Android设备。
要解决这个问题,您可以尝试以下几个步骤:
1.确保您的Android设备已正确连接到计算机,并且USB调试模式已启用。您可以在设备的开发者选项中启用USB调试模式。
2.如果您正在使用模拟器而不是物理设备,请确保模拟器已正确启动,并且与开发环境建立了连接。
3.重新启动Android设备和开发计算机,以确保设备和开发环境之间的连接得到重置。
4.如果您使用的是命令行工具(如adb),请确保adb已正常安装并位于系统路径中。
5.如果您使用的是Android开发工具(如Android Studio),请检查您的项目配置是否正确,并确保选择了正确的目标设备。
如果以上步骤都没有解决问题,可能还有其他潜在的问题,如驱动程序冲突或设备与开发环境的不兼容性。在这种情况下,您可以尝试在开发者论坛或相关社区寻求帮助,以获取更具体的解决方案。
坑二
最开始appuim的运行日志显示的就有输出如下日志,后来发现可能是appuim的版本太老了(装了个最新版的),及后来发现在装AndroidSDK的时候API选项没有勾选(个人觉得大概率是这个),导致输出该信息,把上面两项解决后就没有该信息输出了。
Cannot retrieve git revision for Appium version 1.9.1 from GitHub. Original error: 403 - “{“message”:“API rate limit exceeded for 43.132.98.42. (But here’s the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)”,“documentation_url”:“https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting”}\n”
环境搭好了!!!!! 开心!~耶耶耶( •̀ ω •́ )y✌
Appium Inspector App元素探测工具
UIAutomatorViewer (SDK自带原生元素定位工具)
与Appium Inspector工具的区别在于,
①UIAutomatorViewer 不需要提供启动参数,效率较高。
②Appium Inspector 则需要配置DesiredCapabilities各项参数。
UIAutomatorViewer 位置:
它可能在bin里或在外面
android-sdk-windows\tools\uiautomatorviewer.bat
android-sdk-windows\tools\bin\uiautomatorviewer.bat
UIAutomatorViewer 使用方法:
1.首先保证模拟器是开启的
2.打开uiautomatorviewer.bat文件,会弹出UI Automator Viewer工具界面
3.点击左上角第二个功能按钮(Device Screenshot (uiautomator dump)
4.它就会自动连接设备,成功后显示模拟器界面元素信息
5.想显示指定软件界面就在设备里进入指定界面,然后再点击那个功能按钮就可以了
遇坑一
Error while obtaining UI hierarchy XML file: com.android.ddmlib.SyncException: Remote object doesn’t exist! 获取UI层次结构XML文件时出错:com.android.ddmlib.SyncException:远程对象不存在!
遇坑二
Unexpected error while obtaining UI hierarchy获取UI层次结构时出现意外错误
java.lang.reflect.InvocationTargetException
后续: 我日你个仙人板板,自己电脑本地的开发环境装的同一套SDK,又可以正常打开😡(怒了
我发现问题在哪儿了:
J8,在模拟器微信首界面,连接就报错。其他界面又可以正常显示,这么说来我在公司那套环境也没问题,至于这个首页报错的问题enmmm埋起来有空再查阅~
ok,时隔两天,花了几分钟就搞定了,解决方法如下:(不对劲,这是单个页面的坐标解析,根本问题没解决,看来还是模拟器版本兼容有问题,家里装的是雷电模拟器4,公司是雷电模拟器9)
问题:Unexpected error while obtaining UI hierarchy java.lang.reflect.InvocationTargetException
普遍反映:android8以后sdk自带的uiautomator直接打开,存在截取不到机器界面信息问题。
解决方法:
一.获取uix文件
步骤:
1.adb shell uiautomator dump /sdcard/sc.uix (/sdcard/ 为手机存储目录)
2.adb pull /sdcard/sc.uix
解析:
adb shell uiautomator dump dump出当前窗口UI布局信息
adb pull 从手机上传文件到电脑 (默认保存在当前电脑的用户工作目录下,c:\users\xxx,adb pull的语法 adb pull remote local)
remote 远程目录步骤1保存sc.uix的路径
local 当前pc的路径,不写,即默认
二.截取屏幕
1.将手机的app切换到响应页面
2.在电脑执行如下命令截取屏幕
adb shell screencap -p /sdcard/sc.png (截屏)
adb pull /sdcard/sc.png
解析:
adb shell screencap -p 截屏并保存成png 保存到手机端
三.截图与资源
1.打开uiautomator工具
2.点击左上角的文件夹
3.导入截图
4.导入ui资源
1.adb shell uiautomator dump /sdcard/sc.uix
2.adb pull /sdcard/sc.uix
3.adb shell screencap -p /sdcard/sc.png
4.adb pull /sdcard/sc.png
Appium自动化_前奏
App类型
- NativeApplication(原生app) 是调用设备底层硬件API来使用到对应功能。
无法跨平台 如安卓要安装APK包来使用,IOS要安装IPA包来使用 的app。例如:淘宝京东; - HybridApp(混合app) 是在原生app中嵌入使用web技术。例如:微信(它的主页面功能页是采取原生app开发模式,但它的微信小程序、微信公众号等等是采取的web开发模式也就是HTML5技术)
- WebAPP 是通过浏览器去访问的一个应用程序,它不需要安装,直接通过浏览器方式直接访问。例如:浏览器输入www.jd.com/ 可以直接访问使用
App布局和控件
- 什么是布局?(布局是一个可以容纳别的布局(或者控件)的容器。它就像是一个大的房间,房间里面可以放各种家具(控件),也可以再隔离成更多的房间(放入别的布局)。
- 什么是控件?(界面设计中的控件,就是我们常常看到的按钮 滑动条 文字显示区等等,它们就像房间里的家具,是界面设计的最小单位。
- 共同点:两者有很多共同的地方,例如定义它们的大小、边距等等。
- 小知识:加了Layout说明它就是一个布局,没有加Layout就是一个控件。
Android Debug Birdeg (ADB命令)
- ADB,全名 Android Debug Birdeg 安卓调试桥。
- Android调试桥(adb)是一个通用命令行工具,其允许您与模拟器实例或连接的Android设备进行通信。它可为各种设备操作提供便利,如安装和调试应用,并提供对Unix shell (可用来在模拟器或连接的设备上运行各种命令)的访问。
- 因为安卓底层就是Linux操作系统,所以可以在adb中使用linux命令控制设备【adb shell 进入到安卓手机内部的linux系统命令行中. 是的,Linux命令!直接无敌! exit退出哦】
ADB工作原理
ADB 常用命令
adb help
查看帮助手册adb devices
检测连接到电脑的安卓设备adb pull <手机路径> <本机路径>
从手机中拉取信息放到本地电脑.上adb push <本机路径> <手机路径>
从本地推送信息到手机上去adb shell
登录设备shell模式(命令行的人机界面)adb install xx.apk
安装应用adb uninstall com.tencent.mobileqq
卸载应用adb shell dumpsys activity | find "mFocusedActivity"
查看前台应用包名adb kill-server
终止adb服务adb start-server
启动adb服务,通常在adb出现问题时,结合kill server命令一起使用adb shell am start -n 包名/入口
启动App(‘/’不能少)adb shell pm clear 包名
清除应用的数据和缓存(不可恢复谨慎使用)adb shell input tap x轴坐标 y轴坐标
坐标点击adb shell pm list packages [-s||-3]
列出所有包名 (-s列出系统apk路径及包名)(-3列出用户apk路径以及包名)adb logcat -v time>[路径\xx.txt]
打印日志到指定位置文件
adb shell am start -n 包名/入口 命令详解
《查看入口方法》
D:\QA\android-sdk-windows\build-tools\29.0.3 路径上打开CMD
aapt dump badging [要解析的包体的路径]
如:C:\Users\fang\Desktop\lolm_dev_cn_release_4.4.0.7102_64bit.signed.shell.apk
在输出的包体信息中找到 launchable-activity: name='com.tencent.mm.ui.LauncherUI'
启动名:com.tencent.mm.ui.LauncherUI
《查看包名方法》
adb shell dumpsys window | findstr mFocusedApp 查看当前包名和界面名
例如:
mFocusedApp=AppWindowToken{eb57228 token=Token{8c0684b ActivityRecord{850a51a u0 com.tencent.mm/.plugin.account.ui.LoginSelectUI t4}}}
包名:com.tencent.mm
界面名:.plugin.account.ui.LoginSelectUI
实例:
C:\Users\绯世>adb shell am start -n com.tencent.mm/com.tencent.mm.ui.LauncherUI
Starting: Intent { cmp=com.tencent.mm/.ui.LauncherUI }
模拟器中微新启动成功
小知识:可以通过该命令依次有序打开微信后面的界面
adb shell input tap x轴坐标 y轴坐标 坐标点击命令详解
《快速获取坐标信息的方法》
模拟器或手机的“设置”-关于平板电脑-找到“版本号”连续点击五下-打开开发者选项-进入后打开“指针位置”
《坑1》雷电模拟器在打开"指针位置"后,无效,没有指针显示?
解决方法:
1.打开指针位置
2.打开雷电模拟器的安装目录,找到VMS文件夹,在里面新建 debug.txt 文件
3.重启模拟器,就能看到指针位置显示了
实例: adb shell input tap 1148.0 180.0
效果:x1148.0 y180.0坐标轴位置是微信,命令执行后打开了微信
adb shell pm list packages [-s||-3]
不加携带参数的效果是:列出所有包名
携带-s参数效果是:列出系统apk路径及包名
携带-3参数效果是:列出用户apk路径以及包名
小知识:系统应用是不可卸载的,用户安装的第三方应用是可以卸载的。
小知识2:模拟器或手机 - 设置 - 应用 - 点击某应用 - 有卸载按钮就是第三方应用,没有卸载就是系统自带应用
日志打印实例:
adb logcat -v time>C:\Users\绯世\Desktop\logs\a.txt
Maven
Maven 项目管理工具,用来管理我们项目中所需的繁琐依赖,并提升我们的开发效率。(注Maven只能用于java项目的管理python是不能用的)
小知识
- 可以去
apache -maven-3.5.2\conf\settings.xml
文件中更改存放依赖仓库的位置(可以改到D盘去) - 配置文件中默认的依赖下载路径是国外网址,下载很慢可以把地址改为国内镜像网站。
- 在 idea 和 Eclipse 中使用本地maven时,注意去设置本地maven位置,以及仓库位置,和配置文件位置。
HelloWorld_appium
HelloWorld_App自动化脚本编写步骤
- 导项目依赖 java-client
- 添加配置
- 创建驱动
- 找到页面元素
- 操作页面元素来模拟用户的操作
- 通过断言和日志来查看测试结果
mvnrepository依赖关系网址:https://mvnrepository.com/search?q=java-client
依赖选择策略:选最新最多人用的(用的人数多的较稳定).
《快捷adb指令》
注:该命令要在android-sdk-windows\build-tools\29.0.3>下使用;
解析包体信息,并通过管道符连接查找指定信息
查看包名
aapt dump badging [包路径] | findstr package
查看启动入口
aapt dump badging [包路径] | findstr launchable-activity
《Appium启动,连接了两台及以上设备,无法连接指定设备》
org.openqa.selenium.SessionNotCreatedException
记录一个有意思的异常,以现在掌握的appiumjava自动化的部分知识点来看,已经写入配置项中的设备名,但在电脑启动一个模拟器和连接了一个安卓手机时,它仍不知道启动哪个,导致抛异常。
此时我无法深究底层原因,最快的解决方法就是拔掉那台安卓,成功启动。
C:\Users\v_fsaifang\Desktop\JAVA\JDK\jdk\bin\java.exe "-javaagent:E:\fangyize\package\idea\IntelliJ IDEA Community Edition 2022.3\lib\idea_rt.jar=62665:E:\fangyize\package\idea\IntelliJ IDEA Community Edition 2022.3\bin" -Dfile.encoding=UTF-8 -classpath .
Exception in thread "main" org.openqa.selenium.SessionNotCreatedException: Could not start a new session. Possible causes are invalid address of the remote server or browser start-up failure.
Host info: host: 'v_fsaifang-PC1', ip: '192.168.255.10'
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:620)
at io.appium.java_client.AppiumDriver.startSession(AppiumDriver.java:229)
at org.openqa.selenium.remote.RemoteWebDriver.<init>(RemoteWebDriver.java:163)
at io.appium.java_client.AppiumDriver.<init>(AppiumDriver.java:80)
at io.appium.java_client.AppiumDriver.<init>(AppiumDriver.java:92)
at io.appium.java_client.android.AndroidDriver.<init>(AndroidDriver.java:117)
at AppiumTest.main(AppiumTest.java:21)
Caused by: org.openqa.selenium.WebDriverException: java.lang.reflect.InvocationTargetException
Build info: version: '4.13.0', revision: 'ba948ece5b*'
System info: os.name: 'Windows 10', os.arch: 'amd64', os.version: '10.0', java.version: '1.8.0_333'
《appium·java,匹配到输入框,无法自动写入数据》
org.openqa.selenium.StaleElementReferenceException
解决方法:
文本输入这个问题,这里使用的是automationName引擎,它是导致无法输入的罪主;
我们设置切换 uiautomator2 引擎来解决输入框输入不了数据的问题。
解决方法: dcb.setCapability("automationName","uiautomator2");
报错相关:
SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details.
Exception in thread "main" org.openqa.selenium.StaleElementReferenceException: Cached elements 'By.id: com.tencent.mm:id/bxz' do not exist in DOM anymore
For documentation on this error, please visit:
《页面中有多个resource-id一致的元素时,如何定位》
除了其他定位方式,可以用List<WebElement>
类型集合按序取值的方式来指定唯一性元素。
例如
List<WebElement> list = driver.findElements(By.id("com.tencent.mm:id/bxz"));
list.get(0).sendKeys("Xiao");
list.get(1).sendKeys("123645789");
测试用例断言
- 断言对测试用例是十分重要的 (在app里面每一个页面都有对应的类名,我们可以通过类名来进行断言。
adb shell dumpsys activity | find "mFocusedActivity"
查询当前正在前台运行的类名(界面名)。- AndroidDriver.currentActivity()方法作用,获取当前运行界面名。
- String.equals()方法,判断字符串是否相等。
《坑-当模拟器与Start Inspector Session连接时,无法调用键盘》
这个需要下拉模拟的Android头部列表,将输入法设置为雷电输入法,就可以调用输入法,输入信息了。
《坑-Wechat滑块验证界面不能被识别》
有意思的是通过appium登录微信,每次都会被识别为首次登录,从而跳出图片滑块儿验证页面,而该页面不能被Start Inspector Session(放大镜图标)元素探测工具识别出,进而只能睡眠进程几秒钟手动完成验证。
《WeChat- 我同意上述条款页面,报错》
Exception in thread "main" org.openqa.selenium.WebDriverException: An unknown server-side error occurred while processing the command. Original error: Timed out after 10000 milliseconds waiting for root AccessibilityNodeInfo
Exception in thread "main" org.openqa.selenium.WebDriverException:处理命令时发生未知的服务器端错误。原始错误:等待 root AccessibilityNodeInfo 10000 毫秒后超时
《为什么appium 连接模拟器 启动微信 一直是被认为是新连接,触发微信新设备验证》
https://blog.csdn.net/m0_66106755/article/details/128632786?ops_request_misc=&request_id=&biz_id=102&utm_term=appium%E5%BE%AE%E4%BF%A1%E8%87%AA%E5%8A%A8%E5%8C%96%E6%AF%8F%E6%AC%A1%E9%83%BD%E6%98%AF%E6%96%B0%E8%AE%BE%E5%A4%87%E9%AA%8C%E8%AF%81&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-128632786.142^v95^insert_down28v1&spm=1018.2226.3001.4187
Appuim初始化动作分析 (p20、p21)
需反复观看,达到能独立分析与讲解的程度
- Appium初始化工作主要是通过adb来完成
- 通过对appium的日志分析,它初始化做了哪些操作
查看Appium启动日志,可以看出:
appium初始化过程中,会默认清除app原有的数据。可设置初始化选项不被清理。dcb.setCapability(“noReset”,“True”); //启动app时不清除app原有的缓存和数据。
查看从 uiAutomator 和 uiAutomator2 两种不同引擎启动的日志可以看出:
Appium使用不同底层引擎的区别
- 端口有变更
uiAutomator 引擎默认端口是4724
uiAutomator2更换引擎默认端口是8200 - 初始化流程有变更
如果是默认引擎会向设备推送 bootStrat.jar的jar包,并启动jar包,在4724端口进行监听;
如果是使用的uiAutomator2引擎会往设备中推送两个应用程序io.appium.uiautomator2.server和io.appium.uiautomator2.server.test,这两个应用程序的主要功能是会开启uiautomator2的某个server,这个server会监听appium发送过来的指令,再通过uiautomator2发送给底层去执行。
从此后,所以关于自动化用例的编写,初始化的配置都是使用uiautomator2,它修复了很多Bug,有提供很多新的特性。
Appium常见元素定位
Appium定位 经验总结
定位优先级:
- 最开始没有考虑根据[黄脸表情]的resource-id的下标列表取值,虽然它的元素不唯一,但相比于className(隐藏的很多元素也可能同名)已经少了很多,所以即使的resource-id有元素同名,也应该优先使用该属性下标取值。
解决方法:resource-id单元素定位 > context-desc(accessibility)定位 > text定位 > resource-id多元素下标定位 > Xpath定位 > 坐标定位 - StartInspectorSession放大镜功能,这个工具的元素解析功能并不完善,有很多Bug,例如这个[黄脸表情页面]的元素解析就是依托shit,它的context-desc属性没有被解析出来、甚至于class属性直接被解析错了,导致这边代码报奇怪的错。解决方法:直接从Appsource手动逐步解析,可达到100%精准。
resource-id定位
根据resdouce-id定位,而有多个元素的resdouce-id相同时,会默认匹配第一个元素。
解决方案,把相同id值的元素放到集合中,再通过集合的索引访问。
代码举例:
//resource-id多元素下标方式定位
List<WebElement> HuangLianList = driver.findElements(By.id("com.tencent.mm:id/jnm")); // resource-id 不唯一,有四个,黄脸表情的下标是二,可优化成下标精准定位,如果不取下标直接取值使用的是第一个元素。
HuangLianList.get(1).click();
text定位
//text定位方式,点击【发现】
driver.findElementByAndroidUIAutomator("new UiSelector().text(\"发现\")").click();
className定位
在一个页面中,很多的布局很多的控件,它的class属性都是一样的,所以这种className定位方式是不推荐使用的。
xpath定位
//点击聊天框的[表情] (使用xpath定位)
driver.findElement(By.xpath("//android.widget.ImageButton[@content-desc=\"表情\"]")).click();
Thread.sleep(2000);
xpath定位方式有,绝对定位跟相对定位两种,绝对定位的适用范围不是很好一旦ui元素发生改变绝对路径就需要更改,主要是使用的相对定位。
accessibility定位
举例:
//context-desc(accessibility)方式定位
driver.findElementByAccessibilityId("黄脸表情").click();
坐标定位
Appium元素等待
简单的总结
- 在自动化过程中,元素出现受网络环境,设备性能等多种因素影响。因此元素加载的时间可能不一致,从而会导致元素无法定位超时报错。
- 设置元素等待可以更加灵活的制定等待定位元素的时间,从而增强脚本的健壮性,提高执行效率。
元素等待类型有如下三种:
- 强制等待,固定的等待时间,Thread.sleep();
- 隐式等待,针对全局元素设置等待时间,driver.manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
隐式等待就是在设置的时间范围内整个页面元素加载出来,然后再轮询页面元素直到寻找成功,如果超出时间后仍然未找到元素则抛出NoSuchElementException异常。
同时要注意的是,driver设置的隐式等待时间会对当前driver的整个生命周期都生效,直到调用driver.close()方法。因此,通过driver定位每一个元素都会有隐式等待的时间,这会影响测试脚本执行的效率。 - 显示等待,针对某个元素设置等待时间,可使用WebDriverWait对象;
在设定的时间范围内,每间隔设定的轮询时间定位指定元素,每次间隔的轮询时间内没有定位成功会忽略异常,如果超出设定时间仍未定位成功则抛出异常。且可以使用ExpectedConditions中的多种方法来满足不同的定位需求。
代码详解:
WebDriverWait wait = (WebDriverWait) new WebDriverWait(driver,
Duration.ofSeconds(10),Duration.ofSeconds(1))
.ignoring(NoSuchElementException.class)
.ignoring(WebDriverException.class)
.ignoring(UnreachableBrowserException.class)
.ignoring(ProtocolException.class);
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("continueid")));
element.click();
driver: appiumdriver的初始化实例
timeout:查询条件的最大等待时间,Duration.ofSeconds(10)代表最大的等待时间10秒
sleep:设置查询条件的时间频率,Duration.ofSeconds(1)代表每间隔1秒去定位元素
ignoring:如果每间隔1秒定位元素失败则忽略对应的异常
wait.until:WebDriverWait需要和until方法结合使用,通过调用ExpectedConditions里面的方法来返回你想要的值
三种等待方式举例:
显示等待: 1. 初始WebDriverWait对象 2.匿名内部类对象,实现它的apply()方法
举例:
public static void Discover(AndroidDriver driver) throws InterruptedException {
//强制等待 线程执行到此处等待
// Thread.sleep(3000);
//隐式等待 全局元素加载时间,每个元素都有等份的等待时间,超过规定时间执行下一句代码,不会抛异常.
// driver.manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
//text定位方式,点击【发现】
// driver.findElementByAndroidUIAutomator("new UiSelector().text(\"发现\")").click();发现
//显示等待
WebDriverWait WebWaitDriver= new WebDriverWait(driver, 1);
WebElement Element_discover = WebWaitDriver.until(new ExpectedCondition<WebElement>() {
@NullableDecl @Override
public WebElement apply(@NullableDecl WebDriver webDriver) {
return driver.findElementByAndroidUIAutomator("new UiSelector().text(\"发现\")");
}
});
Element_discover.click();
}
强制等待、隐式等待、显式等待 详解
- 强制等待
//线程强制等待1S
Thread.sleep(1000);
强制等待是利用time模块的sleep方法来实现,最简单粗暴的等待方法。
- 缺点:不能准确把握需要等待的时间(有时候操作未完成,等待就结束了,导致报错;有时候操作已经完成了,但时间还没到,浪费时间),如果再用例中大量使用,会浪费不必要的等待时间,影响用例的执行效率。
- 优点:使用简单,可以在调试时使用。
- Implicit 隐式等待
隐式等待只能作用于元素的等待。如果元素在指定的时间内找到,则不会继续等待,否则在指定时间内未找到元素则抛出NoSuchElementException。
作用域是全局的,跟driver的生命周期一样,一般定义在父类中,只要设置隐式等待后,页面所有的元素都会被绑定这种等待机制,只需设置一次,全局有效(只作用于元素),直到driver实例被关闭。
//隐式等待等待5秒
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
- 缺点:使用隐式等待,程序会一直等待页面加载完成,才会执行下一步操作(有时候页面想要的元素早已加载完成了,但是页面上个别元素还没有加载完成,仍要等待页面全部完成才能执行下一步,使用也不是很灵活)
- 优点:隐式等待对整个driver的周期都起作用,所以只要设置一次即可
- Explicit 显式等待
除了作用于元素等待还可以实现各种场景的等待,例如页面加载等。
智能的等待方式,元素在指定的时间内找到,则不会继续等待,否则抛出TimeOutException。
非全局设置,可以针对不同的元素绑定不同的等待机制。
只有满足显式等待的条件满足(这里是满足页面出现name=wd的元素),测试代码才会继续向后执行后续的测试逻辑,如果超过设定的最大显式等待时间阈值, 这测试程序会抛出异常。
//显式等待3秒
WebDriverWait wait = new WebDriverWait(driver, 3);
//3秒内找不到此元素,TimeOutException报错
wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.name("wd")))
- 缺点:使用相对比较复杂
- 优点:判断准确,不会浪费多余的等待时间,在用例中使用,可以提高执行效率
附:ExpectedConditions其他用法
titleIs判断当前页面的title是否等于预期。
titleContains判断当前页面的title是否包含预期字符串。
urlToBe判断当前页面的url是否等于预期。
urlContains判断当前页面的url是否包含预期字符串。
presenceOfElementLocated判断元素是否出现,出现就通过。
visibilityOfElementLocated判断某个元素是否可见,可见代表元素非隐藏。
elementToBeSelected页面元素处于被选中状态。
————————————————
隐式等待与显示等待结合使用(总等待时间的计算)
一般来说,在项目中会使用隐式等待与显式等待结合的方式,定义完 driver 之后立即设置一个隐式等待,在测试过程中需要判断某个元素属性的时候,再加上显式等待。
若隐式等待时间与显式等待时间不同,当隐式等待时间大于显式等待时间时,最终等待时间等于隐式等待时间;当显式等待时间大于隐式等待时间时,会循环隐式等待时间,最终等待时间大于等于显式等待时间。
详细说明:e.g.
隐式等待时间设置为10秒,A元素的显式等待时间设置为5秒,若没有定位到A元素时,会等待10秒。
隐式等待时间设置为3秒,A元素的显式等待时间设置为7秒,若没有定位到A元素时,会等待3+3+3=9秒。
隐式等待时间设置为9秒,A元素的显式等待时间设置为10秒,若没有定位到A元素时,会等待9+9=18秒。
显示等待- 简便工具类
造轮子参考文章(埋坑
隐式等待 与 显式等待的区别
改造轮子,首先添加AndroidDriverWait.java, 其实是将WebDriverWait的类型改成AndroidDriverWait
封装显示等待Wait类和ExpectedCondition接口
使用WebDriver做Web自动化的时候,org.openqa.selenium.support.ui中提供了非常便捷好用的WebDriverWait类继承FluentWait,所以可以使用FluentWait类中until方法和ExpectedCondition接口进行显示等待的定义,比如某个元素的可见或者可点击等条件,在规定的时间之内等不到指定条件那么就跳出Exception。
最近使用appium做客户端的自动化时发现AppiumDriver无法使用WebDriverWait类,是由于WebDriverWait继承于FluentWait,而WebDriver接口是没有定义findElementByAccessibilityId()、findElementByIosUIAutomation()、findElementByAndroidUIAutomator()的,所以appium想使用像WebDriverWait的显示等待功能,就必须自己封装造轮子了。轮子嘛,俺造不出来。
但基于org.openqa.selenium.support.ui.ExpectedCondition对象封装了一个工具类,后面就不用每个元素都new内部类实现对象使用了,直接调工具类的方法就可以。
package com.fangyize.util;
import io.appium.java_client.MobileBy;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
/**
* @author: 方一泽
* @create-date: 2023/10/13 15:14
*/
public class AppiumElementWait {
// 根据不同的定位方式来进行 Appium 元素显式等待的方法
public static MobileElement waitUntilElementExists(AndroidDriver driver, String locatorType, String locatorValue, int timeoutInSeconds) {
By by = null;
switch (locatorType.toLowerCase()) {
case "id":
by = By.id(locatorValue);
break;
case "class":
by = By.className(locatorValue);
break;
case "xpath":
by = By.xpath(locatorValue);
break;
case "css":
by = By.cssSelector(locatorValue);
break;
case "accessibilityid":
by = MobileBy.AccessibilityId(locatorValue);
break;
case "text":
by = MobileBy.AndroidUIAutomator("new UiSelector().text(\"" + locatorValue + "\")");
break;
default:
System.out.println("Unsupported locator type: " + locatorType);
break;
}
MobileElement element = (MobileElement) new WebDriverWait(driver, timeoutInSeconds)
.until(ExpectedConditions.presenceOfElementLocated(by));
return element;
}
}
Appium-java API详解
CSND - Appium-java API 文章详解
元素定位类findElementByXX
- driver.findElementById(“id”);
id获取方法:利用uiautomater截屏,获取resource id; - driver.findElementByClassName(“className”);
通常通过这种方式获取的view不止一个,需要遍历一遍得到的 views,然后根据条件匹配需要的view。 - driver.findElementByName(“text”);
name获取方法:利用uiautomater截屏,获取text; - driver.findElementsByLinkText (“text”);
针对webview上元素的超链接 - driver.findElementByXPath(“//*[@name=’62’]”);
举例,我要定位下图页面中的这个元素,我采用xpath定位时,可以这样写:
driver.findElementByXPath(“//android.widget.TextView[contains(@text,‘商品描述’)]”). webElement.click();
模拟操作类
driver.runAppInBackground(5); //将当前活跃的应用放在后台运行
driver.hideKeyboard(); //隐藏键盘
driver.lockDevice(); //锁屏
driver.openNotifications(); //打开Android的下拉通知栏
driver.isAppInstalled(“com.example.android.apis”) //判断应用是否安装
driver.installApp(“path/to/my.apk”) //安装应用
driver.removeApp(“com.example.android.apis”) //卸载应用
driver.closeApp() //关闭App
driver.getContextHandles() //可用上下文,context可以理解为可进入的窗口,如果是native则为native_app,如果是webview为对应webview;
driver.context(); //设置上下文 (context),对hybrid app会用到
driver.getAppStringMap(); //获取应用的字符串
driver.pressKeyCode(AndroidKeyCode.HOME); //给设备发送一个按键事件,所有键盘按键值可参考AndroidKeyCode类
driver.pullFile(“Library/AddressBook/AddressBook.sqlitedb”); //从设备中拉出文件
driver.pullFolder(“Library/AddressBook”); //从设备中拉出文件夹
driver.PushFile(“/data/local/tmp/file.txt”, “some data for the file”); //推送文件到设备中去
driver.swipe(75, 500, 75, 0, 0.8) //模拟用户滑动
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(1)); //设置等待时间
driver.navigate().forward(); // 前进
driver.navigate().back(); // 后退
driver.navigate().refresh(); // 刷新
driver.currentActivity(); //当前activity,可用于断言跳转是否正确
driver.startActivity(appPackage,appActivity); //启动其他app的activity
坐标操作,降低适配带来的不适用:
double Screen_X = driver.Manage().Window.Size.Width;//获取手机屏幕宽度
double Screen_Y = driver.Manage().Window.Size.Height;//获取手机屏幕高度
double startX = element.Location.X; //获取元素的起点坐标,即元素最左上角点的横坐标
double startY = element.Location.Y; //获取元素的起点坐标,即元素最左上角点的纵坐标
double elementWidth = element.Size.Width; //获取元素的宽度
double elementHight = element.Size.Height; //获取元素的宽度
报错调试圣经
报错调试经验:
- 语句单词拼写错误,属性名对象名用重了、用多了(命名随意、不规范导致)
- 逐步针对性的检验每句代码的正常运行
- DeBug调试
- 搜索引擎
- 再重复一轮(不要质疑)
Appium手势操作
手势 - 单点滑动
- java-client5.0之前
提供滑动API- AndroidDriver.swipe(),5.0之后swipe()被淘汰了无法使用,但可以参照之前的滑动API自己造一个滑动轮子。
//java-client 4.1.2 API写法
//satrtx,starty 起始点坐标x,y轴值;endx,endy 终止点坐标x,y轴值;duration 从起始点到终止点滑动时间
androidDriver.swipe(startx,starty,endx,endy,duration);
实例:driver.swipe(510,951,510,130,600);
- java-client5.0 之后写法
TouchAction官方解释:该流程是将单个触摸操作链接到整个手势中。例如TouchAction 操作 = new TouchAction(performsTouchActions); action.press(element).waitAction(300).moveTo(element1).release().perform();调用perform()将操作命令发送到移动驱动程序。否则,越来越多的动作可能会被连锁。
其实就是,
往触摸动作对象TouchAction里添加press()、waitAction()、moveTo()、release()动作方法,最后使用perform()方法启动该动作链条,完成滑动效果。
像滑动这种简单的手势,一句代码就能搞定:
touchAction.press(PointOption.point((int)Math.floor(x/4*1.8), (int)Math.floor(y/6*2.8)))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.moveTo(PointOption.point((int)Math.floor(x/4*1.8), (int)Math.floor(y/6*2.1)))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.release().perform();
- 页面滑动轮子
/**
* 页面滑动轮子
* @param startx 初始横坐标
* @param starty 初始纵坐标
* @param endx 末尾横初始
* @param endy 末尾纵坐标
* @param millisecond 滑动所需时间(毫秒
* @param driver AndroidDriver
*/
package com.fangyize.util;
import io.appium.java_client.TouchAction;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.touch.WaitOptions;
import io.appium.java_client.touch.offset.PointOption;
import java.time.Duration;
/**
* @author: 方一泽
* @create-date: 2023/10/16 10:10
*/
public class rewrite_Swipe {
public static void Page_Sliding(int startx, int starty, int endx, int endy, int millisecond, AndroidDriver driver){
/*
ActionChains和TouchAction可以用来模拟点击、双击、滑动等事件。ActionChains用于执行PC端的鼠标移动、按键、拖拽等事件;TouchActions用法与ActionChains类似,可以用来模拟PC和移动端的点击、滑动、拖拽等手势操作。
ActionChains和TouchAction都是将动作存储在队列中,然后执行perform()方法,按队列顺序执行动作。
*/
TouchAction touchAction = new TouchAction(driver);
PointOption startPO = PointOption.point(startx, starty);
PointOption endPO = PointOption.point(endx, endy);
Duration duration = Duration.ofMillis(millisecond);
WaitOptions waitOptions = WaitOptions.waitOptions(duration) ;
//PRESS按下指定坐标位置,moveTo移动至endPO坐标位置,移动时间为waitOptions毫秒,release释放
touchAction.press(startPO).waitAction(waitOptions).moveTo(endPO).release(); //按下press - 滑动moveTo - 抬起release
//执行动作列表
touchAction.perform();
}
}
4.实例: 单点多次滑动,实现解锁九宫格,滑出Z字型图案
public static void Function_touchAction(AndroidDriver driver){
//点击主界面[消息]
AppiumElementWait.waitUntilElementExists(driver,"id",light.getMainInterfaceMessage(),3).click();
//多次滑动 实现解锁九宫格,滑出Z字型图案
TouchAction touchAction = new TouchAction(driver);
PointOption startPO = PointOption.point(141, 955);
PointOption middle1PO = PointOption.point(707, 955);
PointOption middle2PO = PointOption.point(141, 1400);
PointOption endPO = PointOption.point(707, 1400);
Duration duration = Duration.ofMillis(500);
WaitOptions waitOptions = WaitOptions.waitOptions(duration) ;
//PRESS按下指定坐标位置,moveTo移动至endPO坐标位置,移动时间为waitOptions毫秒,release释放
touchAction.press(startPO).waitAction(waitOptions).moveTo(middle1PO).moveTo(middle2PO).moveTo(endPO).release(); //按下press - 滑动moveTo - 抬起release
//执行动作列表
touchAction.perform();
}
手势 - 多点触控
- MultiTouchAction官方定义:
MultiTouchAction 对象是 TouchAction 对象的集合(请记住,TouchAction 对象又是一系列单独的操作)使用 add() 方法添加多个 TouchAction 对象。当调用perform()方法时,所有动作都会发送到performsTouchActions。 PerformsTouchActions 作为多点触摸“执行组”同时执行每个 TouchAction 对象的第一步。从概念上讲,添加到 MultiTouchAction 的 TouchAction 对象的数量等于作为此多手势的一部分同时触摸屏幕的“手指”或其他附件或工具的数量。然后performsTouchActions执行每个TouchAction对象和另一个“执行组”的第二步,第三步,依此类推。在 TouchAction 中使用 waitAction() 操作会占用“执行组”中的一个槽位,因此这些槽位可用于同步复杂的操作。调用perform()将操作命令发送到移动驱动程序。否则,越来越多的动作可能会被连锁。 - 人话
MultiTouchAction类可以模拟用户多点触摸操作,主要包含有add()和perform()两个方法,可以结合TouchAction模拟多根手指滑动效果。
MultiTouchAction是掌心通过add()方法装TouchAction手指,各种动作逻辑如点击、长按、滑动在众多手指里实现,可装多个手指完成高难度动作,最后调用perform()方法使手指动起来。
TouchAction 对象 API:
1. 按压press
press(x=0,y=308).release().perform();
press按压,release松手,perform执行
2. 长按控件longPress
longPress(WebElement el, int x, int y, Duration duration);
3. 点击控件tap
tap(WebElement el, int x, int y);
4. 移动 move_to
moveTo(x,y).perform().release();
5. 暂停 wait()
wait(1000);
6. 取消cancel();
如果该操作已由performsTouchActions 部分完成,则取消该操作。
实例:图片放大
//放大照片,创建手和手指
MultiTouchAction multiTouchAction = new MultiTouchAction(driver);
TouchAction spreadUpward_touch = new TouchAction<>(driver);
TouchAction spreadDownward_touch = new TouchAction<>(driver);
TouchAction ShrinkDown_touch = new TouchAction<>(driver);
TouchAction ContractionUpward_touch = new TouchAction<>(driver);
//获取当前测试设备窗口宽和长
int x = driver.manage().window().getSize().getWidth();
int y = driver.manage().window().getSize().getHeight();
//第一个触点由中心偏左点向左上角移动
spreadUpward_touch.press(PointOption.point((int)Math.floor(x/4*1.8), (int)Math.floor(y/6*2.8)))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.moveTo(PointOption.point((int)Math.floor(x/4*1.8), (int)Math.floor(y/6*2.1)))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.release();
//第二个触点中心偏右向右下角移动
spreadDownward_touch.press(PointOption.point((int)Math.floor(x/4*1.8),(int)Math.floor(y/6*3.2)))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.moveTo(PointOption.point((int)Math.floor(x/4*1.8),(int)Math.floor(y/6*3.8)))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.release();
//第三个触点,下拉收缩再左滑
ShrinkDown_touch.press(PointOption.point((int)Math.floor(x/4*1.8), (int)Math.floor(y/6*2.0)))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.moveTo(PointOption.point((int)Math.floor(x/4*1.8), (int)Math.floor(y/6*3.6)))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.moveTo(PointOption.point((int)Math.floor(x/4*0.6), (int)Math.floor(y/6*3.6)))
.release();
//第四个触点,上拉收缩再左滑
ContractionUpward_touch.press(PointOption.point((int)Math.floor(x/4*1.8), (int)Math.floor(y/6*3.0)))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.moveTo(PointOption.point((int)Math.floor(x/4*1.8), (int)Math.floor(y/6*1.4)))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.moveTo(PointOption.point((int)Math.floor(x/4*0.6), (int)Math.floor(y/6*1.4)))
.release();
//装配TouchAction对象至MultiTouchAction,构成多触点执行,效果动作为:双指放大
multiTouchAction.add(spreadUpward_touch).add(spreadDownward_touch).perform();
Thread.sleep(3000);
//双指缩小左滑,多点触控操作链条执行
new MultiTouchAction(driver).add(ShrinkDown_touch).add(ContractionUpward_touch).perform();
实例:图片or地图放大缩小原理解析:
B到A,C到D,同时运动,达到放大的操作效果;
A到B,D到C,同时运动,达到缩小的操作效果;
Appium常用API
页面跳转
获取当前页面dom结构,获取当前页面类名
重置应用数据、判断App是否安装
向系统发送键值事件
截图功能
获取设备时间、设备DPI、Automationname、横竖屏状态
Appium特殊元素 - toast
这种元素比较特殊,不是以控件的形式在页面结构显示的,不能被常规的页面结构解析工具捕获到。
获取toast的要求:
1.Java-client 5.0+
2.使用UIAutomator2自动化引擎
3.Android系统版本5.0+
获取toast元素信息方式:
//相对路径 *查找所有的标签 contains包含有 @text文本值类型属性 “文本信息”
WebElement toastElement = androidDriver.findElementByxPath("//*[contains(@text,'toast部分信息')]");
//TestNG自带的断言AssertEquals,判断我们的期望值和实际结果是否一致,一致就代表我们的用例通过
assertEquals("",expected);
System.out.print (toastElement.getText());
待发掘的模块
-
还有很多其他API可以再继续拓展
-
java自动化测试工具汇总
-
测试用例正则
Hybrid APP(混合应用)自动化
HybridAPP的定义
Hybrid应用程序:定义-HybridApp(混合app)是在原生app中嵌入使用web技术。例如:微信(它的主页面功能页是采取原生app开发模式,但它的微信小程序、微信公众号等等是采取的web开发模式也就是HTML5技术)。
在 Java 自动化测试中,“Hybrid” 可以指涉到两个相关的概念:
- Hybrid Appium Framework(Hybrid Appium 框架):这是一种使用 Java 编程语言和 Appium 测试框架开发的自动化测试框架,用于测试混合应用程序。该框架旨在结合原生应用和 Web 应用的测试,可以通过 Appium 提供的 API 来访问设备功能和操作应用的用户界面。
- Hybrid Test Automation(混合测试自动化):这是一种在自动化测试中同时测试原生和 Web 方面的混合应用技术。在 Java 自动化测试中,测试人员使用基于 Appium 的测试框架或其他工具来自动化测试混合应用程序,并验证应用程序在原生和 Web 界面上的功能和行为。
总而言之,“Hybrid” 在 Java 自动化测试中可以指涉到开发专门用于测试混合应用的自动化测试框架,也可以表示同时测试原生和 Web 方面的混合应用技术。这些概念与使用 Java 编程语言进行自动化测试和在 Appium 框架中进行移动应用测试密切相关。
Hybrid应用自动化测试介绍
对于Hybrid应用程序的自动化测试方法,Appium官方提供的解决方案是:基于UIAutomator+ChromeDriver实现。
准备工作:
- 准备android 4.4+版本以上的手机/模拟器
- 在app源码中将webview调试模式打开webview.setWebContentsDebuggingEnabled(true)
- 安装UC开发者工具
如何确认要测试的应用程序是HybridAPP(混合的呢)?
方法一:
测试设备 - 设置 - 开发者选项 - 打开"显示布局边界" - 被纵横线条包裹的部分就是原生的页面开发模式(就是控件),再例如打开浏览器中间内容显示的部分就是webView显示页面。
uc-devtools下载与安装 (webUI调试工具)
“UC-DevTools” 是用于 UC 浏览器的开发者工具插件,具有以下几个用途:
-
网页调试:UC-DevTools 为开发者提供了基本的调试工具,如 DOM 元素查看、窗口和控制台调试服务。开发人员可以使用这些工具及时检查和调试他们的页面。
-
性能分析:UC-DevTools 可以监控网页加载过程中的 HTTP 请求和页面渲染性能,并通过视觉化分析工具帮助开发者快速查找性能问题。这对于优化网页性能来说是非常有用的。
-
移动端调试:UC-DevTools 可以在移动设备上进行远程调试,这使得开发人员可以使用自己喜欢的工具来调试移动端浏览器中的页面,就像在桌面浏览器上一样方便。
总的来说,UC-DevTools 是一个优秀的浏览器开发工具,提供了许多有用的功能和工具,可以帮助开发人员更快地构建高质量的 Web 应用程序和网站。
UC开发者平台官网地址:https://developers.uc.cn/
Developer Tool下载地址:https://developers.uc.cn/download/devtool.html
-
谷歌引擎搜索uc-devtools,点第一个(官方链接
-
直接点击页面中的[下载]或下载超链接并不能拉起文件下载(猜想是被什么东西屏蔽了,但是没关系
-
F12 打开开发者工具 - NetWork - All,查看浏览器全部网络日志,再点击[下载]或下载超连接,然后去浏览器网络日志里复制指向文件的http地址,在新的浏览器地址栏里直接Enter就直接下载了。
-
安装好后,[设置]选择本地"Devtools Inspector UI资源"
-
启动模拟器或手机,在[home]模块可以自动识别,点击inspect打开调试
线上App开启webview调试(root)
如果是第三方线上app,一般webview debug开关都是关闭的,这就需要借助第三方工具,才能将debug开关打开。
解决方案:
Xposed+WebviewDebugHook
Xposed是一个框架,能够集成很多功能模块,这些模块能够在不修改APK的情况下,修改APP的运行方式。这里我们需要
WebviewDebugHook模块来开启APP的WebView debug模式。
xposed_3.15.apk 、 WebViewDebugHook.apk PS:谷歌引擎真的很效率,一定要想办法整一下
讲解详情文章: https://www.jianshu.com/p/15726589b5d9
https://blog.csdn.net/ieeso/article/details/112133012
注意:该方案只针对有root权限状态的手机或模拟器,如果没有root是不支持的。
模拟器查看root状态,如下图:
- 安装xposed.APK到测试设备
xposed-55版可用
线上App开启webview调试(非root)
现在的安卓高版本手机,例如安卓8.0、9.0这些非root的手机怎么开启webview调试模式呢?
解决方案:VirtualXposed
VirtualXposed_AOSP_0.17.3.apk
VirtualXposed相当于是在开启一个虚拟空间,在空间中默认装了Xposed,
Hybrid应用自动化脚本
实例:
上下文context切换失败问题
混合应用程序自动化的时候,可能经常会碰到切换context的时候碰到版本不匹配的问题或者是切换失败。
1.找到appium内置chromedriver版本驱动,位置路径:
C:\Users\fang\AppData\Local\Programs\Appium\resources\app\node_modules\appium\node_modules\appium-chromedriver\chromedriver\win\chromedriver.exe
版本匹配对照映射
https://cdn.npmmirror.com/binaries/chromedriver/2.46/notes.txt
2.查看应用内部使用的浏览器版本号
可以使用adb来查询
adb shell pm list package -s
查询系统级应用程序
然后在列表中找到 package:com.android.webview
adb shell dumpsys package activity com.android.webview
3.下载相应的版本号的webDriver,将1.地址里的webDriver替换。
Appium移动端自动化测试-(Java)
文章项目Git地址:https://gitee.com/Yu_Zai/appium-java.git
文章参考Bilibili视频:https://www.bilibili.com/video/BV1QQ4y1y7Z1/
结稿时间: 2023年10月30日18:12:12
遗留问题(埋坑)
Xposed、WebviewDebugHook、VirtualXposed 的资源下载和使用。
https://blog.csdn.net/ieeso/article/details/112133012
https://blog.csdn.net/For_if_while/article/details/106070367
https://juejin.cn/post/6844903624812789773
imooc.com/article/29656