appium介绍
什么是appium?
(官方)appium是一个开源的测试自动化框架,可以与原生的、混合的和移动的web应用程序一起使用。它使用webDriver协议驱动ios、android、windows、mac应用程序。
可以看出appium不仅可以用来测试移动端的app,还可以测试移动端的web以及pc端的桌面应用程序。
appium支持的平台及其测试框架
- ios:xcuitest
- android:uiautomator1、uiautomator2、Espresso
- windows桌面应用:winappdriver
- mac桌面应用:macdriver
- 移动端web
appium特点
- 开源。
- 跨平台:可支持各种移动端和电脑端的测试。
- 多语言:任何语言,只要能访问appium server端的api,都能用来进行测试。
appium设计哲学
- 没有必要为了自动化而重新编译你的应用或以任何方式修改它。就是说测试应用时,没必要修改应用中的源代码来进行测试,因为各个平台都封装了各自系统自带的测试框架,它可以起到桥梁的作用,能将客户端及服务端连接起来;
- 不应该被限制在特定的语言或框架上编写运行测试。也就是说android应用是用java开发的,那么没必要非要用java语言进行测试,可以用python、C、php等,而且也不要仅局限与一个测试框架。理论上只要能调用appium server的api,就能进行测试;
- 移动端自动化框架在自动化接口方面不应该重造轮子。appium server复用了selenium webdriver中的大部分接口,再封装移动端、桌面程序所特有的api。
- 移动端自动化框架应该开源。
appium与selinium的关系
客户端:webdriver模块
我们进入appium的webdriver模块,可以看到它导入了selenium的一些类及方法,比如用于元素定位的By,还有用于与server端建立连接的RemoteConnection,以及向sever发送指令的RemoteCommand。
接下来我们看下appium的webdriver类继承的类:
class WebDriver(
AppiumSearchContext,
ActionHelpers,
Activities,
Applications,
Clipboard,
Context,
Common,
DeviceTime,
Display,
ExecuteDriver,
ExecuteMobileCommand,
Gsm,
HardwareActions,
ImagesComparison,
IME,
Keyboard,
Location,
LogEvent,
Network,
Performance,
Power,
RemoteFS,
ScreenRecord,
Session,
Settings,
Sms,
SystemBars
)
我们点进AppiumSearchContext这个类看看,可以看到它又继承了webdriver这个类:
而这个webdriver就是属于selenium的:
从这里可以看出,Chrome跟appium都是继承了selenium中的webdriver,然后根据浏览器或手机特性再去封装各自的代码。
客户端:mobileby
我们可以再看下appium的定位元素的类:MobileBy,可以看到它也是继承了selenium的By类,然后再封装了手机所特有的定位方式:
from selenium.webdriver.common.by import By
class MobileBy(By):
IOS_PREDICATE = '-ios predicate string'
IOS_UIAUTOMATION = '-ios uiautomation'
IOS_CLASS_CHAIN = '-ios class chain'
ANDROID_UIAUTOMATOR = '-android uiautomator'
ANDROID_VIEWTAG = '-android viewtag'
ANDROID_DATA_MATCHER = '-android datamatcher'
ANDROID_VIEW_MATCHER = '-android viewmatcher'
WINDOWS_UI_AUTOMATION = '-windows uiautomation'
ACCESSIBILITY_ID = 'accessibility id'
IMAGE = '-image'
CUSTOM = '-custom'
服务端:协议
在服务端上,appium其实也是复用了selenium的。selenium webdriver使用的协议是json wire protocol,它是自动化web浏览器的标准,并且成为了w3c的工作草案;而appium则在复用json wire protocol大部分api的基础上,根据手机的特性又封装了自己的api,然后向w3c提出了自己的方案:Mobile Json Wire Protocol。
appium原理
appium原理与selenium大体上是相同的,只是在server端与测试主体之间的联系有些差异。
首先,在客户端,我们可以用java、python、php语言编写appium的脚本,然后就会通过mobile json wire protocol协议(也就是调用appium server端的接口)将请求以json的数据传输格式发送给appium server。接到请求后,appium server就会根据客户端代码中command_executor、desired_capabilities两个参数里的具体数据来决定发送请求给哪个平台。如果desired_capabilities参数中指明了平台为安卓,就会调用android sdk tools中的adb与android端的真机或模拟器建立连接,然后使用android sdk tools中的测试框架uiautomator来操作平台上的app。操作完成后,把信息(错误信息 or 成功信息)返回给appium server,server再将信息中的重点内容传给客户端的控制台,用户也可直接在appium server的桌面程序上的控制台看到信息。
appium实战
desiredcapabilities
什么是desiredcapabilities?
desiredcapabilities就是在appium客户端在跟appium服务端进行通信时所需要传递的一些参数,也就是告诉服务端要测试的对象是什么?它长什么样?要到哪里找?
desiredcapabilities参数
下面摘录了一些desiredcapabilities的参数:
通用
|
automationName
|你想使用的自动化测试引擎|
Appium
(默认) 或Selendroid
||
platformName
|你要测试的手机操作系统|
iOS
,Android
, 或FirefoxOS
||
platformVersion
|手机操作系统版本|例如:
7.1
,4.4
||
deviceName
|使用的手机类型或模拟器类型|
iPhone Simulator
,iPad Simulator
,iPhone Retina 4-inch
,Android Emulator
,Galaxy S4
, 等。在 iOS 上,这个关键字的值必须是使用
instruments -s devices
得到的可使用的设备名称之一。在 Android
上,这个关键字目前不起作用。||
app
|
.ipa
or.apk
文件所在的本地绝对路径或者远程路径,也可以是一个包括两者之一的.zip
。Appium会先尝试安装路径对应的应用在适当的真机或模拟器上。
针对Android系统,如果你指定
app-package
和app-activity
(具体见下面)的话,那么就可以不指定app
。会与
browserName
冲突 |比如/abs/path/to/my.apk
或
http://myapp.com/app.ipa
||
browserName
|需要进行自动化测试的手机 web 浏览器名称。
如果是对应用进行自动化测试,这个关键字的值应为空。
|iOS 系统上可以用 ‘Safari‘ ,Android 系统上可以用‘Chrome‘, ‘Chromium‘, 或
‘Browser‘。||
newCommandTimeout
|设置命令超时时间,单位:秒。
达到超时时间仍未接收到新的命令时 Appium 会假设客户端退出然后自动结束会话。|比如
60
|
autoLaunch
|Appium是否需要自动安装和启动应用。默认值
true
|true
,false
||
language
|(Sim/Emu-only) 设定模拟器 ( simulator / emulator ) 的语言。|如:
fr
||
locale
|(Sim/Emu-only) 设定模拟器 ( simulator / emulator ) 的区域设置。|如:
fr_CA
||
udid
| 连接的物理设备的唯一设备标识|如:1ae203187fc012g
||
orientation
|(Sim/Emu-only) 在一个设定的方向模式中开始测试|
LANDSCAPE
(横向) 或PORTRAIT
(纵向) ||
autoWebview
|直接转换到 WebView 上下文。 默认值
false
、|true
,false
||
noReset
|不要在会话前重置应用状态。默认值
false
。|true
,false
||
fullReset
|(iOS) 删除整个模拟器目录。(Android) 通过卸载——而不是清空数据——来重置应用状态。在 Android
上,这也会在会话结束后自动清除被测应用。默认值
false
|true
,false
|
android特有
|
appActivity
|你要从你的应用包中启动的 Android Activity 名称。
它通常需要在前面添加
.
(如:使用.MainActivity
而不是MainActivity
)
|MainActivity
,.Settings
||
appPackage
|你想运行的Android应用的包名|
比如
com.example.android.myApp
,com.android.settings
||
appWaitActivity
|你想要等待启动的 Android Activity 名称|
SplashActivity
||
deviceReadyTimeout
|设置等待一个模拟器或真机准备就绪的超时时间|
5
||
androidCoverage
|用于执行测试的 instrumentation 类。
作为命令
adb shell am instrument -e coverage true -w
的-w
参数。|
com.my.Pkg/com.my.Pkg.instrumentation.MyInstrumentation
||
enablePerformanceLogging
|(仅适用于 Chrome 和 webview) 开启 Chromedriver 的性能日志。 (默认
false
) |true
,
false
||
androidDeviceReadyTimeout
|等待设备在启动应用后准备就绪的超时时间。以秒为单位。|如
30
||
androidDeviceSocket
|开发工具的 socket 名称。只有在被测应用是一个使用 Chromium 内核的浏览器时需要。 socket 会被浏览器打开,然后
Chromedriver 把它作为开发者
工具来进行连接。|如chrome_devtools_remote
||
avd
|需要启动的 AVD (安卓虚拟设备) 名称。|如
api19
||
avdLaunchTimeout
|以毫秒为单位,等待 AVD 启动并连接到 ADB 的超时时间。(默认值
120000
)|300000
||
avdReadyTimeout
|以毫秒为单位,等待 AVD 完成启动动画的超时时间。(默认值
120000
)|300000
||
avdArgs
|启动 AVD 时需要加入的额外的参数。|如
-netfast
||
useKeystore
|使用一个自定义的 keystore 来对 apk 进行重签名。默认值
false
|true
orfalse
||
keystorePath
|自定义 keystore 的路径。默认: ~/.android/debug.keystore|如
/path/to.keystore
||
keystorePassword
|自定义 keystore 的密码。|如
foo
||
keyAlias
|key 的别名 |如
androiddebugkey
||
keyPassword
|key 的密码 |如
foo
||
chromedriverExecutable
|webdriver 可执行文件的绝对路径 (如果 Chromium 核心提供了对应的 webdriver, 应该用它代替 Appium
自带的 webdriver) |/abs/path/to/webdriver
||
autoWebviewTimeout
|以毫秒为单位,等待 Webview 上下文激活的时间。默认值
2000
| 如4
||
intentAction
|用于启动 activity 的 intent action。 (默认值
android.intent.action.MAIN
)| 如
android.intent.action.MAIN
,android.intent.action.VIEW
||
intentCategory
|用于启动 activity 的 intent category。 (默认值
android.intent.category.LAUNCHER
) | 如
android.intent.category.LAUNCHER
,
android.intent.category.APP_CONTACTS
|
intentFlags
|用于启动 activity 的标识 ( flags ) (默认值
0x10200000
) | 如0x10200000
|
optionalIntentArguments
|用于启动 activity 的额外 intent 参数。请查看 Intent
参数 | 如
--esn <EXTRA_KEY>
,--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE>
|
dontStopAppOnReset
|在使用 adb 启动应用时不要停止被测应用的进程。如果被测应用是由另一个锚应用(anchor app)创建的,请把这个参数设置为
false
以允许锚应用 在使用 adb
启动被测应用时保持运行。换句话说,当dontStopAppOnReset
设为true
时,我们在adb shell am start
命令中不会包含-S
标志。
如果这个参数没有被设置或设为false
,我们会加入-S
标志。默认值:false
|true
或false
||
unicodeKeyboard
|使用 Unicode 输入法。默认值
false
|true
或false
||
resetKeyboard
|在设定了
unicodeKeyboard
关键字的 Unicode 测试结束后,重置输入法到原有状态。如果单独使用,将会被忽略。默认值
false
|true
或false
||
noSign
|跳过检查和对应用进行 debug 签名的步骤。只能在使用 UiAutomator 时使用,使用 selendroid 是不行。默认值
false
|true
或false
||
ignoreUnimportantViews
|调用 uiautomator 的函数
setCompressedLayoutHierarchy()
。由于 Accessibility
命令在忽略部分元素的情况下执行速度会加快,这个关键字能加
快测试执行的速度。被忽略的元素将不能够被找到,因此这个关键字同时也被实现成可以随时改变的 *设置 ( settings ) * 。默认值
false
|true
或
false
||
disableAndroidWatchers
|关闭 android 监测应用无响应(ANR)和崩溃(crash)的监视器,这能够在 android 设备/模拟器上减少 cpu
使用量。这个参数只能在使用
UiAutomator 时工作,在使用 selendroid 时无效。默认值:false
。|true
或者false
||
chromeOptions
|允许传入 chrome driver 使用的 chromeOptions 参数。
必传参数
- platformName:android还是ios;
- deviceName:设备名称,可通过adb devices命令得到;
- app:需要安装的apk包的路径。如果已经安装好了,一般不会再用这个参数;
- automationName:自动化测试引擎,一般写UiAutomator1,如果不写会默认用UiAutomator2,可能会出现问题;
- appPackage&appActivity:这两个参数一般都是连着用的。当apk包已经安装好了之后,就通过这两个命令来指定移动端上的app。appPackage是app的包名,它是唯一的;appActivity则是能与用户进行交互的应用窗体标识。这连个参数可通过以下方式获取到:
- (常用)aapt dump badging apk的本地路径
- (常用)adb shell dumpsys activity|find “mFocusedActivity”
真机或模拟器上打开app,即可获取包名和activity:
- adb logcat | findStr -i displayed
- adb shell am monitor
以aapt命令为例。执行后,找到packagename即对应的是appPackage;launchable-activity即对应的是appActivity。
代码示例
from appium import webdriver
caps = {
"platformName": "Android",
"deviceName": "emulator-5554",
"automationName": "UiAutomator1",
"appPackage": "com.xxzb.fenwoo",
"appActivity": "com.xxzb.fenwoo.activity.addition.WelcomeActivity"
}
driver = webdriver.Remote(
command_executor="http://127.0.0.1:4723/wd/hub",
desired_capabilities=caps
)
appium server是如何运行的?
这里我们写了一段appium的代码,来看下appium server是如何运行的:
from appium import webdriver
caps = {
"platformName": "Android",
"deviceName": "emulator-5554",
"automationName": "UiAutomator1",
"appPackage": "cc.forestapp",
"appActivity": "cc.forestapp.applications.SplashActivity",
"noReset": True
}
driver = webdriver.Remote(
command_executor="http://127.0.0.1:4723/wd/hub",
desired_capabilities=caps
)
from appium.webdriver.common.mobileby import MobileBy
element = driver.find_element(MobileBy.ID,"cc.forestapp:id/button_text")
element.click()
在appium服务端启动时,把日志级别设成debug,会实时打印出执行日志,从日志中我们可以了解到appium server的执行逻辑,以及一些很重要的信息。
appium server执行时分为三个阶段:准备阶段、执行阶段、结束阶段。
准备阶段
这一阶段主要工作是发送请求给appium server,然后server调用adb执行adb指令连接目标并打开app一级自动化测试框架uiautomator。
首先,appium客户端会调用api:/wd/hub/session,并把代码中的参数:command_executor、desired_capabilities作为传参。appium server接收到请求后,会校验请求参数有没有问题,没问题的话就会拿command_executor、desired_capabilities作为参数创建一个android的驱动对象:
上面这些都准备好之后,就会去检查adb的环境有没有准备好,然后查看sdk的版本号并使用最新的一个:
接着就会调用adb devices指令,查看有几台设备并连接到指定设备:
接着会执行一个adb shell的命令:adb -P 5037 -s emulator-5554 shell getprop ro.build.version.sdk去获取目标sdk的版本号,这里获取到的是22,而我们这边的sdk用的是28,可以向下兼容:
如果获取的目标sdk版本要高于本地的sdk,则会报错。
接着会去查看代码中appPackage指定的apk包是否在目标上,并且等待设备出现及尝试ping通目标:
接着会去获取appium.settings包的信息,如果没有就会去安装:
这个appium settings的程序就是控制台中的BaseDriver,它负责接收接收appium server传过来的指令并执行指令操作app:
接下来这一段可能是去获取appium settings的相关信息并进行相关配置:
接着去获取设备平台的相关信息,包括版本等:
接着会重置apk,也就是将apk恢复到新安装的状态,此时打开apk可能会有新手指引等界面出现。如果想跳过重置apk,可以在desired_capabilities中设置一个参数:noReset。
接着就回去打开uiautomator:
执行阶段
这个阶段主要就是执行用户编写的appium脚本对应的指令。
首先会准备一下bootstrap:
关于bootstrap,这里摘录下别的博主的描述:
那么我们应该怎么来给bootstrap做一个定义呢?我不知道官方有没有做一个定义,但是按照我自己的语言,我会这样来定义它:
Bootstrap是Appium运行在安卓目标测试机器上的一个UiAutomator测试脚本,该脚本的唯一一个测试方法所做的事情是在目标机器开启一个socket服务器来把一个session中Appium从PC端过来的命令发送给UiAutomator来执行处理。
这个定义说明了bootstrap在appium和uiautomator中究竟处于一个什么样的角色:
首先,它是一个uiautomator的测试脚本,它的入口类Bootstrap继承于UiAutomatorTestCase,所以UiAututomator可以正常运行它,它也可以正常的使用uiautomator的方法,这个就是appium的命令可以转换成uiautomator的命令的关键
其次,它是一个socket服务器,它专门监听4724端口过来的appium的连接和命令数据,并把appium的命令转换成uiautomator的命令来让uiautomator进行处理
最后,它处理的是appium从pc端过来的命令,而非一个文件。这在初次接触appium的朋友是很容易困惑的,以为appium是整个脚本文件发送到目标机器再由bootstrap进行分析处理的,事实并非如此。
接着客户端就会发送执行请求(如定位元素)给bootstrap,然后bootstrap执行之后的结果返回给客户端:
结束阶段
这一阶段主要是执行完appium的指令后,清理环境,关闭basedriver、androiddriver、AndroidBootstrap、uiautomator。
inspector
inspector有什么用?
inspector是appium自带的一款模拟手机操作并辅助测试的一款工具,相当于浏览器中的F12。在自动化测试当中,我们一般用来进行元素定位。
如何打开一个应用?
点击appium server的右上角第一个图标:
打开之后,可以看到有一栏熟悉的名字:Desired Capabilites:
没错,与代码中的desired_capabilities参数是对应起来的,只要把里面的参数全部填到界面上即可。
另外,为了下次使用方便,还可以保存配置好的Desired Capabilities。
界面介绍
输入完Desired Capabilities并点击start session后,就会打开新的界面:
这个界面跟浏览器中的F12非常地相似,分为4个部分:
- 红色部分:界面;
- 黄色部分:xml树,相当于F12的element中的dom。这里可以看出app的元素是用xml标记的,而pc端的web是用html标记的;
- 蓝色部分:元素属性。定位到的某个元素的具体属性。在app中,元素的属性与html的元素属性是不一样的。html中的元素可以有某个属性也可以没有,且一些特殊的元素还有特殊的属性(如a标签的href属性);但app中,每个元素的属性都是相同的,都有21个属性,只是属性的值不一样罢了;
- 绿色部分:操作栏。可以对红色部分的界面做出操作,然后xml数及元素属性会根据操作作出对应的展示。从左往右按钮依次是:
-
Select Element:选择元素。相当于浏览器中的选择元素:
-
Swipe By Coordinates:选择滑动的起始和结束位置。当app上需要左右滑动切换页面时就可以用到这个
-
Tap By Coordinates:使得手机界面变换可操作状态,可以点击界面的元素
-
Back:模拟Android的返回键
-
Refresh Source & Screenshot:刷新页面,用来重新获取手机当前界面。有时真机/模拟器上的画面与inspector中的画面不同步,就可以通过刷新同步画面
-
Start Recording:录制操作。录制在界面上的操作,然后可以转换成自动化脚本
-
Search for element:校验定位表达式。输入元素表达式,看下能找出多少个元素
-
Copy XML Source to Clipboard:复制XML树
-
Quit Session & Close Inspector:退出当前Session
这里比较常用的操作是:选择元素、校验定位表达式。
-
appium官方文档
学appium最好的方式就是去看appium官网中的相关文档:
http://appium.io/