男儿欲遂平生志,六经勤向窗前读。这篇文章主要讲述Appium自动化-Android相关的知识,希望能为你提供帮助。
appium介绍
特点
appium 是一个自动化测试开源工具,支持 ios 平台和 android 平台上的原生应用,web应用和混合应用。
- “移动原生应用”是指那些用iOS或者 Android SDK 写的应用(Application简称app)。APP应用客户端,它包含了所有UI元素、框架逻辑等。只有数据存储在云端。
- “移动web应用”是指使用移动浏览器访问的应用(appium支持iOS上的Safari和Android上的 Chrome)。
- “混合应用”就是html5 APP,混合APP通常由两部分组成,HTML5云网站+APP应用客户端,这里的APP应用客户端实际只是个架子,里面的UI元素和逻辑,都是存储在云端的,每次打开APP时,去云端获取数据呈献给手机用户。说白了混合APP就是将web页面嵌套到了APP应用客户端中,额米尺打开APP都要向HTML云端服务请求数据,所以会产生不小的数据流量,并且如果没有网络,会导致无法看到HTML APP。这就是常说的H5页面。
Appium是一个开源、跨平台的自动化测试框架,它适用于Native APP\\Hybrid APP。
Appium有个比较好的设计哲学,简单的说就是不要为了移动端自动化测试而单独开发一套Api接口,所以Appium也是基于Selenium的webdriver进行了扩展,扩展了一些操作移动端的Api接口。
Appium最牛之处在于支持跨平台操作,实际上我们在测试时是启动了Appium Service,这个Service可以放到任意的机器上,供公司的自动化测试人员共同使用。
Appium原理
Appium既然是基于selenium做的二次扩展,那么appium也是一个经典的Client-Server的设计模式,我们的Code就是狭义的客户端,Server端与Selenium不同,Selenium直接测试浏览器Web页面,将浏览器作为服务端。而Appium的服务端是我们自己启动的Appium-Server。数据与操作命令传递与Selenium Api接口相同,遵守REST设计风格的Api接口。
Appium工作流
文章图片
Appium跨平台
文章图片
安装依赖一、 Java安装和配置
二、 Node.js安装和配置
三、 Android studio安装和配置
四、配置SDK环境变量
五、 Appium安装和配置
cnpm的安装
npm install cnpm -g --registry=https://registry.npm.taobao.org cnpm -v---来测试是否成功安装
cnpm安装appium
cnpm i -g appium
https://www.cnblogs.com/sjl179947253/p/11947246.html
文章图片
adb命令 获取UI信息
adb kill-server //结束adb服务 adb start-server //启动adb服务 adb devices //获取adb设备列表
cmd-连接安卓机ip
adb connect xxx //创建与xxx的设备连接
adb connect 127.0.0.1:62001
adb connect 127.0.0.1:62005
...
然后运行tools\\uiautomatorviewer.bat文件
文章图片
设备安装命令
adb install | -r < apkName> -r 覆盖安装文件 -s 可以指定设备
adb install filepath
eg:
默认安装adb install “C:\\F\\20190415112024069.apk”
覆盖安装adb install -r “C:\\F\\20190415112024069.apk”
指定设备安装adb -s 127.0.0.1:62001 install “C:\\F\\20190415112024069.apk”
文章图片
文章图片
package和activity区别
packagePackage包,在我们app中这个package是唯一的,就像你的身份证号码一样,做自动化时就需要知道这个package(注意和apk文件包名不同)
通过UIAuto演示不同app的包名
Activity在android中,activity是所有程序的根本,所有程序都运行在activity中,是开发遇到最频繁,Android最基本的模块之一
在Android的程序中,activity一般代表手机屏幕的一屏,把手机比作一个浏览器,activity就相当于一个网页,在activity总可以添加一些button、Checkbox控件,与网页的概念相当类似
一百一个android应用是由多个activity组成的,这个activity之间可以进行相互跳转。
与网页跳转不一样的是,activity之间的跳转可能返回值
Activiy生命周期:产生、运行、销毁
调用方法:onCreate(创建)、onStart(激活)、onResume(恢复)、onPause(暂停)、onStop(停止)、onDestroy(销毁)、onRestart(重启)
Activity获取研发提供
aaptaapt即Android Asset Packaging Tool,在SDK的build-tools目录下。该工具可以查看、更新、创建ZIP格式的文档附件(zip、jar、apk)。也可以将源文件编译成二进制文件。
获取命令:
aapt dump badging xxxx.apk # 获取apk包信息 aapt dump badging xxxx.apk | find "launchable-activity" # 精确查看其中某项包信息
获取
【launchable-activity: name=\'com.was.api.WasActivity\' label=\'\' icon=\'\'】
注意:首先要进入aapt命令的目录下执行
文章图片
文章图片
尝试运行一个app 启动appium服务
文章图片
文章图片
用pycharm编辑启动设置
安装依赖库
pip install Appium-python-Client
准备启动配置的信息
from appium import webdriverdesired_capabilities = { "deviceName":"127.0.0.1::62001", # 手机唯一ID "platformVersion":"5.1.1", # 手机版本 "platformName":"Android", # 设备类型 "appPackage":"com.tal.kaoyan", # 包名 "appActivity":"com.tal.kaoyan.ui.activity.SplashActivity" # 入口 }driver = webdriver.Remote(\'http://127.0.0.1:4723/wd/hub\',desired_capabilities)
运行
文章图片
文章图片
文章图片
如上已经启动起来了
SDK自带定位工具介绍
在SDK目录tools文件夹下
文章图片
文章图片
定位方式 ClassNameAndroid
Android的class属性对应ClassName定位方式,ClassName一般都是会重复的,可以通过index来获取需要的元素。(从0开始查找dom树中的同名class属性)
iOS
iOS的type属性对应CLassName定位方式,ClassName一般都是会重复的,可以通过index来获取需要的元素。(从0开始查找dom树中的同名class属性)
IDAndroid
Android的resource-id对应ID定位方式,这个id也可能存在重复情况,可以通过index来获取需要的元素。(从0开始查找dom树中的同名resource-id属性)
使用appium-desktop来获取元素时,如果提示有id的定位方式,则可以只接获取,代表唯一。
XPATHAndroid
Android的Xpath定位与PC的XPATH定位大同小异,可以通过相对路径的定位方式定位,区别在于,这里相对路径定位的//后只可以接Android的class属性或*。(//android.widget.Button[@text="登 录"])
iOS
iOS10 以上使用XCUITest框架后,原生框架不支持XPATH,Appium进行了转换,速度很慢不建议使用。
AccessibilityIdAndroid
Android的content-desc属性对应AccessibilityId定位方式,这个content-desc属性专门为残障人士设置,如果这个属性不为空则推荐使用。
iOS
iOS的label和name属性都对应AccessibilityId定位方式,如果有则推荐使用。
AndroidUIAutomatorAndroid的源生测试框架的定位方式,定位速度快。推荐使用牢记常用的几种。
# 这个在运行时,调用的是Android自带的UI框架UiAutomator的Api # 介绍几个简单常用的,text、className、resource-id # text # 匹配全部text文字 driver.find_element_by_android_uiautomator(\'new UiSelector().text("手机号")\') # 包含text文字 driver.find_element_by_android_uiautomator(\'new UiSelector().textContains("机")\') # 以text什么开始 driver.find_element_by_android_uiautomator(\'new UiSelector().textStartsWith("手")\') # 正则匹配text driver.find_element_by_android_uiautomator(\'new UiSelector().textMatches("^手.*")\')# className driver.find_elements_by_android_uiautomator(\'new UiSelector().className("android.widget.TextView")\') # classNameMatches driver.find_elements_by_android_uiautomator(\'new UiSelector().classNameMatches("^android.widget.*")\')# resource-id、resourceIdMatches类似我们html id 这个可能重复, driver.find_element_by_android_uiautomator(\'new UiSelector().resourceId("com.syqy.wecash:id/et_content")\') # description driver.find_element_by_android_uiautomator(\'new UiSelector().description("S 日历")\') # descriptionStartsWith driver.find_element_by_android_uiautomator(\'new UiSelector().descriptionStartsWith("日历")\') # descriptionMatches driver.find_element_by_android_uiautomator(\'new UiSelector().descriptionMatches(".*历$")\')
常用API接口 获取元素系列
# Android对应content-desc IOS对应accessibility identifier # content-desc是给残障人士定义的特殊字段 driver.find_element_by_accessibility_id(\'content-desc\')# ID定位于selenium不同,可能存在重复的问题。 # appium-desktop抓取元素时如果出现有id,则可以直接用。 # resource-id可能出现重复,需要具体看下有多少个。 driver.find_element_by_id(\'resource-id\')# 对应class字段,有可能存在多个相同class。 # 多个相同class,每次获取只取第一个遇到的元素。 driver.find_element_by_class_name(\'class\')# Xpath路径定位,与Web的Xpath定位在有一点点区别 # Native App的定位以class为基准,主要用到参数有text、resource-id、index driver.find_element_by_xpath(\'//android.widget.EditText[@text="手机号"]\')# 这个在运行时,调用的是Android自带的UI框架UiAutomator的Api # 介绍几个简单常用的,text、className、resource-id # text # 匹配全部text文字 driver.find_element_by_android_uiautomator(\'new UiSelector().text("手机号")\') # 包含text文字 driver.find_element_by_android_uiautomator(\'new UiSelector().textContains("机")\') # 以text什么开始 driver.find_element_by_android_uiautomator(\'new UiSelector().textStartsWith("手")\') # 正则匹配text driver.find_element_by_android_uiautomator(\'new UiSelector().textMatches("^手.*")\')# className driver.find_elements_by_android_uiautomator(\'new UiSelector().className("android.widget.TextView")\') # classNameMatches driver.find_elements_by_android_uiautomator(\'new UiSelector().classNameMatches("^android.widget.*")\')# resource-id、resourceIdMatches driver.find_element_by_android_uiautomator(\'new UiSelector().resourceId("com.syqy.wecash:id/et_content")\')# description driver.find_element_by_android_uiautomator(\'new UiSelector().description("S 日历")\') # descriptionStartsWith driver.find_element_by_android_uiautomator(\'new UiSelector().descriptionStartsWith("日历")\') # descriptionMatches driver.find_element_by_android_uiautomator(\'new UiSelector().descriptionMatches(".*历$")\')# Native App 暂不能使用 driver.find_element_by_tag_name(\'\')# Native App 暂不能使用 driver.find_element_by_partial_link_text(\'\')# Native App 暂不能使用 driver.find_element_by_link_text(\'\')# Native App 暂不能使用 driver.find_element_by_css_selector(\'\')
获取手机分辨率
size = driver.get_window_size() # 获取手机屏幕大小,分辨率 print(size) # 得到的是一个字典,从而获取到手机的宽和高 height = size.get(\'height\') width = size.get(\'width\') print("height:",height) print("width:",width)
文章图片
滑屏、触控和拖拽
# 滚动处理 # elementObj1 目标滚动元素,elementObj2 起始滚动元素 # 底层通过action操作,与web ui相反,origin_el为目标元素,destination_el为起始元素 # 通过模拟手势可以看出 从下面的元素移动到上面的元素 # action.press(origin_el).move_to(destination_el).release().perform() # web elementObj1为要移动的元素 elementObj2为移动到某个元素 # action.click(elementObj1).move_to_element(elementObj2).release().perform() driver.scroll(elementObj1, elementObj2)#通过坐标实现滑屏操作 # 从手机的左上角开始,横坐标为x轴,纵坐标为y # 分辨率不同,坐标不相同,所以需要根据屏幕的大小做比例运算 driver.swipe(start_x, start_y, end_x, end_y,duration=None)# 拖拽 # elementObj1源元素,elementObj2目标元素 # 通过long_press实现 driver.drag_and_drop(elementObj1, elementObj2)# 多点触控 模拟手指点击 # tap模拟按住坐标多少秒,[(x,y)] 多个坐标则写多个元祖。参数2为按住多少秒。ms(毫秒)为单位 driver.tap([(x, y)], 5000)
拖拽
# 拖拽 # elementObj1源元素,elementObj2目标元素 # 通过long_press实现 driver.drag_and_drop(elementObj1, elementObj2)
触控
# 多点触控 模拟手指点击 # tap模拟按住坐标多少秒,[(x,y)] 多个坐标则写多个元祖。参数2为按住多少秒。ms(毫秒)为单位 driver.tap([(x, y)], 5000)
操作app
# 返回是否安装了对应包名的App True 或者 False driver.is_app_installed(\'com.syqy.wecash\')# 关闭初始化信息中的App driver.close_app()# 启动初始化信息中的App driver.launch_app()# 安装app 参数为软件的绝对路径 driver.install_app(r\'C:\\Users\\bjhouyafan\\Desktop\\tester\\appUi\\wecash.apk\')# 卸载app 参数接收appPackageName driver.remove_app(\'com.syqy.wecash\')# 获取当前打开的app名 driver.current_activity# 启动某一个包的,某一个入口 # 参数接受appPackage,appActivity # 可以省略多个步骤直接到达指定测试的位置 driver.start_activity(\'com.android.browser\',\'.BrowserActivity\')# 每隔0.5秒判断一次当前的app名称是否是activityName # WebDriverWait driver.wait_activity(\'activityName\', 5, 0.5)# 将启动的app退出到后台,多少秒后在切回app driver.background_app(\'m\')# 清除应用数据缓存,相当于卸载重装 driver.reset()
NATIVE_APP 与 WEBVIEW 上下文操作
# 返回当前session中的app类型 driver.current_context# 返回当前app的类型 # WEBVIEW 或 WEBVIEW # 底层实际调用current_context driver.context# 获取app所有的类型 # 有WEBVIEW的则以list的形式展示两个 driver.contexts# app类型切换 参数接收app类型 # appium对selenium的switch_to的扩展 # 增加了MobileSwitchTo,继承了selenium的switch_to driver.switch_to.context(\'context\')
KeyCode操作
# 隐藏键盘 driver.hide_keyboard()# 发送按键码,仅按一下,与press_keycode相同 # 可接收str或int的code码 driver.keyevent(3)# 发送按键码,仅按一下,可接收str或int的code码 driver.press_keycode(24)# 发送一个长按的按键码,接收参数必须是int的code码 driver.long_press_keycode(25)
网络
# 返回当前手机的网络状态 driver.network_connection# 导入 from appium.webdriver.connectiontype import ConnectionType# WIFI ConnectionType.WIFI_ONLY# 数据流量 ConnectionType.DATA_ONLY# 飞行模式 ConnectionType.AIRPLANE_MODE# 无网络模式 ConnectionType.NO_CONNECTION# 全部都打开 ConnectionType.ALL_NETWORK_ON# 设置 网络 driver.set_network_connection(ConnectionType.AIRPLANE_MODE)
操作输入法
# 返回Android上可用的输入法 driver.available_ime_engines # 返回当前输入法的包名 driver.active_ime_engine # 是否启动了输入法 True or False driver.is_ime_active() # 切换输入法 参数接收available_ime_engines中任意一个输入法包名 driver.activate_ime_engine(driver.available_ime_engines[0]) # 关闭当前输入法 driver.deactivate_ime_engine()
其他
# 锁定手机多少秒 仅IOS driver.lock()# 摇手机 driver.shake()# 打开通知栏 仅Android driver.open_notifications()# 获取连接手机的当前时间 driver.device_time# 开启或关闭手机定位服务 driver.toggle_location_services()
操作H5页面【Appium自动化-Android】https://www.cnblogs.com/zz-yy/p/8931568.html
简单练习
# from appium import webdriver from time import sleepdesired_capabilities = { "deviceName":"8PBIQ8EEROUW4TZH", # 手机唯一ID "platformVersion":"6", # 手机版本 "platformName":"Android", # 设备类型 "appPackage":"com.tencent.mobileqq", # 包名 "appActivity":"com.tencent.mobileqq.activity.SplashActivity", # 入口 "noReset":"true" }driver = webdriver.Remote(\'http://127.0.0.1:4723/wd/hub\',desired_capabilities) sleep(2)login_loc = \'com.tencent.mobileqq:id/btn_login\' # driver.find_element_by_id(login_loc).click() driver.find_element_by_android_uiautomator(\'new UiSelector().text("登录")\').click() sleep(2)name_loc = \'请输入QQ号码或手机或邮箱\' # driver.find_element_by_accessibility_id(name_loc).send_keys(\'475355108\') driver.find_element_by_android_uiautomator(\'new UiSelector().text("QQ号/手机号/邮箱")\').send_keys(\'475455888\') sleep(1)pwd_loc = \'密码 安全\' driver.find_element_by_accessibility_id(pwd_loc).send_keys(\'ccc88888888\') sleep(1)loginBtn_loc = \'登 录\' driver.find_element_by_accessibility_id(loginBtn_loc).click()sleep(3) driver.quit()
推荐阅读
- Android VideoView未解决,动态读取权限BottomNavigationView的用法
- 其它测试(回归测试冒烟测试APP测试联调测试确认测试随机测试安全测试探索性测试)
- Android Framework 学习(屏幕刷新机制)
- Appium代码实例
- 把flutter项目作为aar添加到已有的Android工程上
- uniapp 实现底部导航栏 - tabBar的使用方法
- APP自动化定位元素——Android SDK——打开uiautomatorviewer.bat文件——查看元素
- Android studio 3.6 ToolBar问题
- Ruby redo和retry语句用法详解