一、Appium简介
Appium是一个开源测试自动化框架,可用于原生(native app),混合(html)和移动Web应用程序()测试。 它使用WebDriver协议驱动iOS,Android和Windows应用程序。
二、Appium优势
- 可以跨平台同时支持android、ios
- 支持多种语言,java、python、php、Ruby等
- 不用为复杂的环境发愁
- 有selenium经验,可直接上手
三、环境依赖
- Appium-desktop -->二合一 node.js和 appium
- Appium-Python-Client
- Python
- JDK
- Andriod SDK
3.1、Android SDK 安装配置
下载地址: http://tools.android-studio.org/index.php/sdk
根据自己的PC平台下载对应版本即可
安装完成后需要配置环境变量:ANDROID_HOME 之前adb操作的时候已经配置过,把下面的再添加进去就可以
四、Appium Desktop
Appium Desktop是一款适用于Mac,Windows和Linux的开源应用程序,它以美观而灵活的用户界面为您提供Appium自动化服务器的强大功能。 它是几个Appium相关工具的组合:
- Appium Server的图形界面。 可以设置选项,启动/停止服务器,查看日志等…也不需要使用Node 的NPM来安装Appium,因为Node运行时与Appium Desktop捆绑在一起。
- 可以使用Inspector查看应用程序的元素,获取有关它们的基本信息,并与它们进行基本的交互。
【注】:Appium Desktop与Appium不同。 Appium Desktop是Appium的图形前端,带有其他工具。 Appium Desktop以其自己的节奏发布,并拥有自己的版本控制系统。 就像国内很多定制的Android系统有自己版本号,但是都是基于一个Android系统版本封装的。版本号不一定与Andriod原生系统版本号一致。
如:魅族的flyme6.0系统的内核是Android 5.1
4.1、安装Appium-desktop
下载地址:https://github.com/appium/appium-desktop/releases
Appium-desktop工具其实也封装了Appium server和Node.js依赖环境以及获取元素的图形化工具,本身是一个单独的工具;Appium-desktop主界面包含三个菜单Simple,Advanced、Presets
4.1.1、Simple
4.1.1.1、host
设置Appium server的ip地址,本地调试可以将ip地址修改为127.0.0.1
4.1.1.2、port
设置端口号,默认是4723不用修改
4.1.1.3、start server
启动appium server
4.1.2、Advanced
高级参数配置修改,主要是一些Android和iOS设备,log路径等相关信息的配置
4.1.3、persets
将Advanced中的一些配置信息作为预设配置
五、Capability
5.1、什么是Capability
- desired capability的功能是配置Appium会话。他们告诉Appium服务器您想要自动化的平台(版本)和应用程序。
- Desired Capabilities是一组设置的键值对的集合,其中键对应设置的名称,而值对应设置的值。(如:“platformName”: “Android”)Desired Capabilities主要用于通知Appium服务器建立需要的Session。
5.2、session
Appium的客户端和服务端之间进行通信都必须在一个Session的上下文中进行。客户端在发起通信的时候首先会发送一个叫作“Desired Capabilities”的JSON对象给服务器。服务器收到该数据后,会创建一个session并将session的ID返回到客户端。之后客户端可以用该session的ID发送后续的命令。
5.3、常用Capability配置
Capability官方完整文档
如果有了解过Capability的人会发现一个问题,其实他主要分成了三部分:公共部分、ios部分、android部分,如果android想用ios的那是不可能的,so,老老实实去了解每个平台有哪些,他们的作用是什么
5.4、Capability启动App演示
5.4.1、New Session Window 会话建立
- Automatic Server 本地AppiumServer服务
- Custom Server:例如,如果要针对运行在网络中另一台计算机上的Appium服务器启动Inspector会话,这很有用。
- Sauce Labs:如果您无法访问机器上的iOS模拟器,则可以利用Sauce Labs帐户在云中启动Appium会话。
- TestObject:您还可以利用TestObject的真实设备云来进行真机测试。
- headspin:使用远程设备来创建会话。
5.4.2、desired capability参数Json
使用adb shell dumpsys window | findstr mCurrentFocus 命令查看当前运行的包名和Activity更清晰一些
adb shell dumpsys activity activities >C:\Users\xxx\Desktop\aa.txt
查看activty等信息:appt dump badging E:\xxx.apk
查找初始加载页面: appt dump badging E:\xxx.apk | findstr “lauchable-activity”
5.4.3、常见问题
——那些踩过的坑
[MJSONWP] Encountered internal error running command: Error: Error occured while starting App. Original error: Permission to start activity denied.
activity在清单文件里面没添加android:exported=”true”的话,不能直接打开对应的activity,需要从启动页activity打开。 exported属性就是设置是否允许activity被其它程序调用
新的会话窗口允许您构造一组desired capabilities,用于启动Appium会话。您可以针对当前运行的Appium Desktop服务器(默认的)启动一个会话,或者您可以针对各种其他端点启动一个会话。
因为不需要使用Appium Desktop自己的服务器,您可以在不启动Appium Desktop服务器的情况下进入新的会话窗口。只需点击“File”(Windows / Linux)或“Appium”(Mac),然后选择“New Session…”,它将打开新的会话窗口,而不必启动本地服务器。在这种情况下,将禁用附加到本地服务器。
六、案例——考研帮kaoyan3.1.0.apk
6.1、运行前检查事项
1.检查设备是否连接
2.检查Appium server是否启动
3.检查Capability配置信息是否正确
6.2、Appium Settings、Unlock
1.首次启动Appium会在设备上安装2个守护app,Appium Settings和Unlock 部分设备系统由于权限的问题(如:三星S6 edge+)需要用户手动确认安装,否则不安装守护App会导致脚本运行失败,安装好后不要随意卸载这两个App。
- Unlock :用于解锁手机弹窗提示
- Appium Setting:Appium守护app
2.from appium import webdriver 中的webdriber模块和selenium中的webdriver模块不一样!
{python安装路径}}\Lib\site-packages\appium\webdriver #webdriver模块源码路径
6.3、Appium报错&解决方案
——那些年我们踩过的坑
6.3.1、Appium服务未启动
urllib.error.URLError: <urlopen error [WinError 10061] 由于目标计算机积极拒绝,无法连接。
【解决方案】点击启动Appium按钮后,出现[Appium] Welcome to Appium v1.7.2提示后再运行脚本
6.3.2、会话冲突
error: Failed to start an Appium session, err was: Error: Requested a new session but one was in progress
【报错分析】 之前的会话没有关闭,然后又运行了测试实例,也没有设置覆盖.
【解决方案】 重新停止appium服务,开启Appium服务 在AdVance界面勾选Allow Session Override选项 ,重启Appium 测试结束在AfterClass加driver.quit()
6.3.3、未安装java环境
selenium.common.exceptions.WebDriverException: Message: A new session could not be created. (Original error: 'java -version' failed. Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "java -version"
6.3.4、设备未连接
selenium.common.exceptions.WebDriverException: Message: An unknown server-side error occurred while processing the command. Original error: Could not find a connected Android device.
【解决方案】由于设备未连接,或者连接后未开启USB Debug。需要重新连接设备即可。
6.3.5、更换手机设备后如下对应的属性要记得更新,否则无法正常运行脚本。
desired_caps['platformVersion'] = 'XXX'
desired_caps['deviceName'] = 'Galaxy S6 edge+'
6.3.6、launchable activity 值写错
Activity used to start app doesn't exist or cannot be launched! Make sure it exists and is a launchable activity
【解决方案】
- launchable activity 写错更正即可。
- 如果是存在此activity,则一定是AndroidMainfest.xml.xml中,当前activity设置的属性exported=false,表示当前activity无法被外部程序唤醒。(appium无法唤醒此类)需要研发人员去修改参数。
6.3.7、系统权限问题
Failure [INSTALL_FAILED_USER_RESTRICTED])
【解决方案】
1.USB安装管理权限限制,关闭即可。
2.开启安装允许未知来源app选项
6.3.8、服务异常
An unknown server-side error occurred while processing the command” while opening the App
【解决方案】重新启动Appium服务
6.4、元素定位
与Web自动化测试一样,app自动化测试过程中最重要一个环节就是元素定位,只有准确定位到了元素才能进行相关元素的操作,如输入、点击、拖拽、滑动等。appium提供了许多元素定位的方法,如id定位、name定位、class定位、层级定位等等… 接下来将会给大家来实践运用这些定位技巧。
6.4.1、id
日常生活中身边可能存在相同名字的人,但是每个人的身份证号码是唯一的,在app界面元素中也可以使用id值来区分不同的元素,然后进行定位操作。Appium中可以使用 find_element_by_id() 方法来进行id定位。
# 使用uiautomatorviewer查看 或者appium inspector查看
driver.find_element_by_id('android:id/button2')
driver.find_element_by_id('com.tal.kaoyan:id/tv_skip')
6.4.1.1、思考
- 如果安装的版本最新的包,或者升级到了最新的版本,则启动后没有升级弹窗元素该如何处理?
- 跳过引导页面首次启动和非首次启动场景该如何处理?
6.4.1.2、异常捕捉
if语句判断无法生效(因为找不到元素会报错),但是我们发现一个突破口,那就是捕捉NoSuchElementException异常。
from appium import webdriver
from selenium.common.exceptions import NoSuchElementException
desired_caps={}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '6.0'
desired_caps['deviceName'] = 'BTF4C17216005082'
desired_caps['app']='E:\kaoyan3.1.0.apk'
desired_caps['appPackage']='com.tal.kaoyan'
desired_caps['appActivity']='com.tal.kaoyan.ui.activity.SplashActivity'
desired_caps['noReset']='True'
driver=webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
driver.implicitly_wait(5)
def check_cancelBtn():
print("check_cancelBtn")
try:
cancelBtn = driver.find_element_by_id('android:id/button2')
except NoSuchElementException:
print('no CancelBtn')
else:
cancelBtn.click()
def check_skipBtn():
print("check_skipBtn")
try:
skipBtn = driver.find_element_by_id('com.tal.kaoyan:id/tv_skip')
except NoSuchElementException:
print('no skipBtn')
else:
skipBtn.click()
check_cancelBtn()
check_skipBtn()
注:
- send_keys()传入中文时需要在capability中配置如下内容:
desired_caps['unicodeKeyboard']="True"
desired_caps['resetKeyboard']="True"
设置之后会有Appium的输入法守护来执行输入操作
- 使用Appium做了输入操作之后,如果出现输入法无法唤起,可以在系统设置——语言和输入法——将当前输入法替换为系统输入法或者其他输入法。
6.4.2、name
根据name进行定位,对于android来说,就是text属性
from find_element.capability import *
driver.find_element_by_name('请输入用户名')
driver.find_element_by_name('登录')
说明:由于text稳定性不是很好,所以appium 1.5开始废弃了该方法。
6.4.3、class
classname定位是根据元素类型来进行定位,但是实际情况中很多元素的classname都是相同的,
如上例中登录页面中的用户名和密码都是className属性值都是:“android.widget.EditText” 因此只能定位第一个元素也就是用户名,而密码输入框就需要使用其他方式来定位,这样其实很鸡肋.一般情况下如果有id就不必使用classname定位。
by_classname.py
from find_element.capability import driver
driver.find_element_by_class_name('android.widget.EditText').send_keys('付腾飞')
driver.find_element_by_class_name('android.widget.EditText').send_keys('1234')
driver.find_element_by_class_name('android.widget.Button').click()
6.4.4、相对定位
相对定位是先找到该元素的有对应属性的父元素节点,然后基于父元素进行元素定位。
测试案例
不使用id元素定位方式,在新用户注册界面点击添加头像按钮。
from find_element.capability import driver
driver.find_element_by_id('com.tal.kaoyan:id/login_register_text').click()
root_element=driver.find_element_by_id('com.tal.kaoyan:id/activity_register_parentlayout')
root_element.find_element_by_class_name('android.widget.ImageView').click()
6.4.5、xpath
xpath定位是一种路径定位方式,主要是依赖于元素绝对路径或者相关属性来定位,但是绝对路径xpath执行效率比较低(特别是元素路径比较深的时候),一般使用比较少。通常使用xpath相对路径和属性定位。
表达式 | 描述 |
---|---|
/ | 从根节点选取 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 |
nodename | 选取此节点的所有子节点 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
通配符 | 描述 |
---|---|
* | 匹配任何元素节点 |
@* | 匹配任何属性节点 |
node() | 匹配任何类型的节点 |
from find_element.capability import driver
driver.find_element_by_xpath('//android.widget.EditText[@text="请输入用户名"]').send_keys('ftf1234')
driver.find_element_by_xpath('//*[@class="android.widget.EditText" and @index="3"]').send_keys('ftf123456')
driver.find_element_by_xpath('//android.widget.Button').click()
# driver.find_element_by_xpath('//*[@class="android.widget.Button"]').click()
扩展资料:xpath语法
6.4.6、UIAutomator定位
UIAutomator元素定位是 Android 系统原生支持的定位方式,虽然与 xpath 类似,但比它更加好用,且支持元素全部属性定位.定位原理是通过android 自带的android uiautomator的类库去查找元素。 Appium元素定位方法其实也是基于Uiautomator来进行封装的。
使用方法 find_element_by_android_uiautomator() 可以运用UiAutomator元素定位。
6.4.6.1、id定位
id定位是根据元素的resource-id属性来进行定位,使用 UiSelector().resourceId()方法即可。
from find_element.capability import driver
driver.find_element_by_android_uiautomator\
('new UiSelector().resourceId("com.tal.kaoyan:id/login_email_edittext")').send_keys('ftf1234')
driver.find_element_by_android_uiautomator\
('new UiSelector().resourceId("com.tal.kaoyan:id/login_password_edittext")').send_keys('ftf123456')
driver.find_element_by_android_uiautomator\
('new UiSelector().resourceId("com.tal.kaoyan:id/login_login_btn")').click()
6.4.6.2、text定位
text定位就是根据元素的text属性值来进行定位,new UiSelector()
driver.find_element_by_android_uiautomator\('new UiSelector().text("请输入用户名")').send_keys('ftf1234')
6.4.6.3、class name定位
与Appium class定位方式一样,也是根据元素的class属性来进行定位。
driver.find_element_by_android_uiautomator\('new UiSelector().className("android.widget.EditText")').send_keys('zxw1234')