Appium中文使用指南

前言:

前段时间学习Android自动化测试,网上关于Appium的介绍和学习资料比较散乱,大多不齐全,官网API看的也是一脸懵,所以决定自己整理一份文档,供自己学习,也希望能帮助到正在学习Appium和自动化测试的朋友们。
在本地写了一个md文档之后,放到了GitHub上维护。鉴于简书的颜值,我决定转战简书写博客,但是简书的Markdown貌似不只是页内锚点跳转,所以本文的所有锚点都无效了。文章有些长,需要pdf的朋友们可以去我的GitHub上拿。

为什么选择Appium

学习Android自动化测试的时候,看了google官方推荐的几个工具和测试框架,比如Espresso和UI Automator,其中在使用UI Automator来做UI自动化测试的时候,发生一点问题:向控件输入字符的时候无效!不管是中文还是英文都无法输入,而且最终竟然还显示测试通过。经过多方搜索都找不到解决办法,最终还是选择Appium,并且Appium是跨平台的,可以测试ios和Android还有webApp;其中Android平台上是基于UI Automator的,所以可以无缝切换使用UI Automator的API。


作者:GeeJoe
邮箱:geejoe_developer@outlook.com
GitHub:https://github.com/GeeJoe/AppiumDoc
更新时间:2017-09-14 16:44

Appium使用指南


环境配置

  • 安装Nodejs
  • 安装Appium
  • 配置IDE

安装NodeJs

Nodejs官网 下载最新版本的NodeJs并直接安装。安装完毕后,打开命令行,输入 node -v ,出现类似下面的信息说明安装成功。

V4.0.0

安装Appium

Appium 官网 载和你所使用系统一致的版本进行安装。

验证安装

当确认Appium安装完毕后,我们可以通过 appium-doctor 的命令来检查当前appium安装是否完善,当前的JDK、SDK等环境是否配置正确。

如果 appium-doctor 返回的内容是有错的,请根据返回的具体的提示,将你的环境搭建完善。
如果返回的结果类似如下,说明安装成功

...
...
Android Checks were successful.
All Checks were successful

需要注意的是,如果你是通过安装包安装的,使用 appium-doctor 命令时必须切换到
C:\Program Files (x86)\Appium\node_modules.bin 目录;为了方便,需要把.bin目录添加到环境变量Path中。

配置IDE

配置AndroidStudio

  1. 新建一个项目,AndroidStudio新建项目会自动生成一个 androidTest 包和 test
  2. 下载 Appium java clientSelenium Java standalone server 两个库对应的 jar 包
  3. 将两个jar包复制到项目module里的 libs 目录
  4. 选择两个jar包,单击鼠标右键,选择Add as Library
  5. 配置成功,可以在 test 包中新建测试用例了

启动Appium Server

通过图形化界面启动

双击Appium图标,打开Appium应用

appium_start.png

点击箭头所指的按钮即可启动

从命令行启动

只需要一个命令:

appium

若显示如下信息则启动成功:

info: Welcome to Appium v1.4.16 (REV ae6877eff263066b26328d457bd285c0cc62430d)
info: Appium REST http interface listener started on 0.0.0.0:4723
info: Console LogLevel: debug

若需要退出,按下 ctrl+c 即可

配置启动参数

启动时可以手动配置参数,了解所有参数,可查阅 Appium 官方文档

标志 默认值 描述 例子
-a , --address 0.0.0.0 监听的 ip 地址。注意在图形化界面上默认值为 127.0.0.1 --address 0.0.0.0
-p , --port 4723 监听的端口。需要启动多个 appium server 进行并行测试时需要保证每个server 的监听端口不一样。 --port 4723
--log-timestamp false 在日志输出里显示时间戳
--local-timezone false 在日志输出的时间戳使用本地时间
-g , --log null 将日志输出到指定文件 --g /path/to/appium.log
--session-override false 允许 session 被覆盖 (冲突的话)
--command-timeout 60 默认所有会话的接收命令超时时间 (在超时时间内没有接收到新命令,自动关闭会话)。 会被新的超时时间覆盖

这些参数同样可以在图形界面设置:

setting.png

学习DesiredCapabilities

DesiredCapabilities简介

DesiredCapabilities 携带了一些配置信息,在启动session的时候是必须提供,如启动模式、apk/app配置、package/activity配置、浏览器配置、键盘配置等。

Desired Capabilities关键字

Desired Capabilities的重要作用是在启动时传递信息给Appium Server。
下表中列举了Appium常用的 Desired Capabilities 关键字。

描述
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 的 instruments -s devices 命令可返回一个有效的设备的列表。在 Andorid 上虽然这个参数目前已被忽略,但仍然需要添加上该参数
app 本地绝对路径远程 http URL 所指向的一个安装包(.ipa,.apk,或 .zip 文件)。Appium 将其安装到合适的设备上。请注意,如果您指定了 appPackageappActivity 参数(见下文),Android 则不需要此参数了。该参数也与 browserName 不兼容。 /abs/path/to/my.apkhttp://myapp.com/app.ipa
browserName 做自动化时使用的浏览器名字。如果是一个应用则只需填写个空的字符串 'Safari' 对应 iOS,'Chrome', 'Chromium', 或 'Browser' 则对应 Android
newCommandTimeout 用于客户端在退出或者结束 session 之前,Appium 等待客户端发送一条新命令所花费的时间(秒为单位) 例如 60
language (Sim/Emu-only) 为模拟器设置语言 例如 fr
locale (Sim/Emu-only) 为模拟器设置所在区域 例如 fr_CA
udid 连接真机的唯一设备号 例如 1ae203187fc012g
orientation (Sim/Emu-only) 模拟器当前的方向 竖屏横屏
autoWebview 直接转换到 Webview 上下文(context)。默认值为 false true, false
noReset 在当前 session 下不会重置应用的状态。默认值为 false true, false
fullReset (iOS)删除所有的模拟器文件夹。(Android) 要清除 app 里的数据,请将应用卸载才能达到重置应用的效果。在 Android, 在 session 完成之后也会将应用卸载掉。默认值为 false true, false

Android 独有

描述
appActivity Activity 的名字是指从你的包中所要启动的 Android acticity。他通常需要再前面添加. (例如 使用 .MainActivity 代替 MainActivity MainActivity, .Settings
appPackage 运行的 Android 应用的包名 com.example.android.myApp, com.android.settings
appWaitActivity 用于等待启动的 Android Activity 名称 SplashActivity
appWaitPackage 用于等待启动的 Android 应用的包 com.example.android.myApp, com.android.settings
appWaitDuration 用于等待 appWaitActivity 启动的超时时间(以毫秒为单位)(默认值为 20000) 30000
deviceReadyTimeout 用于等待模拟器或真机准备就绪的超时时间 5
androidCoverage 用于执行测试的 instrumentation 类。 传送 -w 参数到如下命令 adb shell am instrument -e coverage true -w com.my.Pkg/com.my.Pkg.instrumentation.MyInstrumentation
enablePerformanceLogging (仅适用于 Chrome 与 webview)开启 Chromedriver 的性能日志。(默认值为 false true, false
androidDeviceReadyTimeout 用于等待设备在启动应用后准备就绪的超时时间。以秒为单位。 例如 30
androidInstallTimeout 用于等待在设备中安装 apk 所花费的时间(以毫秒为单位)。默认值为 90000 例如 90000
adbPort 用来连接 ADB 服务器的端口(默认值为 5037 5037
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 truefalse
keystorePath 自定义 keystore 的路径, 默认路径为 ~/.android/debug.keystore 例如 /path/to.keystore
keystorePassword 自定义 keystore 的密码 例如 foo
keyAlias key 的别名 例如 androiddebugkey
keyPassword key 的密码 例如 foo
chromedriverExecutable webdriver 可执行文件的绝对路径(如果 Chromium 内嵌一个自己提供的 webdriver,则应使用他去替换掉 Appium 自带的 chromedriver) /abs/path/to/webdriver
autoWebviewTimeout 用于等待 Webview 上下文(context)激活的时间(以毫秒为单位)。默认值为 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)应用所创建的,设置该参数为 false 后,就允许钩子(anchor)应用的进程在使用 adb 启动被测应用期间仍然存在。换而言之,设置 dontStopAppOnResettrue 后,我们在 adb shell am start 的调用中不需要包含 -S标识(flag)。忽略该 capability 或 设置为 false 的话,就需要包含 -S 标识(flag)。默认值为 false truefalse
unicodeKeyboard 使用 Unicode 输入法。 默认值为 false truefalse
resetKeyboard 在设定了 unicodeKeyboard 关键字的 Unicode 测试结束后,重置输入法到原有状态。如果单独使用,将会被忽略。默认值为 false truefalse
noSign 跳过检查和对应用进行 debug 签名的步骤。仅适用于 UiAutomator,不适用于 selendroid。 默认值为 false truefalse
ignoreUnimportantViews 调用 uiautomator 的函数 setCompressedLayoutHierarchy()。由于 Accessibility 命令在忽略部分元素的情况下执行速度会加快,这个关键字能加快测试执行的速度。被忽略的元素将不能够被找到,因此这个关键字同时也被实现成可以随时改变的 设置 ( settings )。 默认值为 false truefalse
disableAndroidWatchers 禁用 android 监视器(watchers)。监视器用于监视应用程序的无响应状态(anr)和崩溃(crash),禁用会降低 Android 设备或模拟器的 CPU 使用率。该 capability 仅在使用 UiAutomator 时有效,不适用于 selendroid,默认设置为 false truefalse
chromeOptions 允许对 ChromeDriver 传 chromeOptions 的参数。了解更多信息请查阅 chromeOptions chromeOptions: {args: ['--disable-popup-blocking']}
recreateChromeDriverSessions 当移除非 ChromeDriver webview时,终止掉 ChromeDriver 的 session。默认设置为 false truefalse
nativeWebScreenshot 在 web 的上下文(context),使用原生(native)的方法去截图,而不是用过代理的 ChromeDriver。默认值为 false truefalse
androidScreenshotPath 在设备中截图被保存的目录名。默认值为 /data/local/tmp 例如 /sdcard/screenshots/
autoGrantPermissions 让Appium自动确定您的应用需要哪些权限,并在安装时将其授予应用。默认设置为 false truefalse

Session简介

Session 是指我们的测试脚本从打开应用到最终执行完毕关闭应用的整个过程。
对于测试脚本,从申请到退出一个 session 的整个过程如下:

//打开一个应用,也可以称为“申请一个Session”
AndroidDriver driver = new AndroidDriver<>(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);

//此处的所有代码处于一个Session中
...

//关闭应用,也可以称为“退出一个Session”
driver.quit();

元素定位

方法名 参数 描述
findElementByName String
元素name属性
通过元素name属性查找,在Android中一般可以用text代替
findElementByAndroidUIAutomator String
Ui Automator查找代码
使用UI Automator来查找元素
findElementByClassName String
类名,要写全路径:android.weight.TextView
通过元素类名查找
findElementById String
元素id,android:id/title
通过元素id查找
findElementByAccessibilityId String
元素的contentDescription属性
通过contentDescription属性查找
findEelementByXPath String
元素的XPath
通过XPath查找
findElementByCssSelector WebView专用
findElementByLinkText WebView专用
findElementByPartialLinkText WebView专用

注: 每一个方法对应着一个findElementsBy***方法,后者返回一个Element集合List,表示一个符合查找规则的一个Element集合

findElementByName

在Android中,没有合适的方法可以找到控件的Name属性,但是大多数情况下可以用控件的text代替name。

findElementByAndroidUIAutomator

使用UI Automator查找控件:

WebElement el = driver.findElementByAndroidUIAutomator("new UiSelector().text(\"Add note\")");

更多关于UI Automator定位元素的方法,详见:UI Automator定位元素

findElementByClassName

WebElement el = driver.findElementByClassName("android.weight.TextView");

findElementById

WebElement el = driver.findElementById("android:id/title");

如果目标设备的API Level低于18则UIAutomatorViewer不能获得对应的Resource ID,只有等于大于18的时候才能使用。

findElementByAccessibilityId

WebElement el = driver.findElementByAccessibilityId("menu_add_note_description");

Accessibility ID在Android上面就等同于contentDescription

findEelementByXPath

WebElement el = driver.findElementByXPath("//android.widget.TextView[contains(@text,'Add note')]");  

xPath是一种路径,在uiautomatorviewer中可以查看当前页面的元素层级,XPath就是用来描述这种层级关系的一种路径表达方式,比如,下图中的例子,想找列表中第二个note1,则可以这样写:

UIAutomatorviewer.png
WebElement el = driver.findElementByXPath("//android.widget.LinearLayout[1]/android.widget.FrameLayout/android.widget.ListView/android.widget.TextView[contains(@index,0)]");  

findElementByCssSelector

这个方法是针对WebView容器下面的控件定位的,因为现在针对的是Native App暂时还没有用到,所以先标记下,今后需要的时候加上去。

findElementByLinkText

这个方法是针对WebView容器下面的控件定位的,因为现在针对的是Native App暂时还没有用到,所以先标记下,今后需要的时候加上去。

findElementByPartialLinkText

这个方法是针对WebView容器下面的控件定位的,因为现在针对的是Native App暂时还没有用到,所以先标记下,今后需要的时候加上去。

UI Automater定位元素

UI Automator中主要通过UISelector类查找元素

通过文本信息定位

返回值 方法名 说明 用法
UiSelector text(String text) 根据“控件text属性的内容”构造出UiSelector对象 例如,一个控件text的值是“发现”,UiSelector s = new UiSelector().text("发现");
UiSelector textContains(String text) 根据“控件text属性包含的内容”构造出UiSelector对象 同上例子:UiSelector s = new UiSelector().textContains("现");
UiSelector textMatches(String regex) 根据“控件text属性正则表达式的内容”构造出UiSelector对象 正则表达式语法参考网上资料
UiSelector textStartsWith(String text) 根据“控件text属性开始的内容”构造出UiSelector对象 同上例子:UiSelector s = new UiSelector().textStartsWith("发");

通过description定位

返回值 方法名 说明
UiSelector description(String desc) 根据“控件content-desc属性的内容”构造出UiSelector对象
UiSelector descriptionContains(String desc) 包含**
UiSelector descriptionMatches(String regex) 正则
UiSelector descriptionStartsWith(String desc) 以**开始

通过ResourceId定位

返回值 方法名 说明
UiSelector resourceId(String id) 根据资源id获取对象,例如:UiSelector s = new UiSelector().resourceId("com.tencent.mm:id/b8m")
UiSelector resourceIdMatches(String regex) 根据资源id的正则表达式获取对象

通过类名定位

返回值 方法名 说明
UiSelector className(String className) 根据控件类名找到对象
UiSelector classNameMatches(String regex) 根据类名的正则表达式获取对象
UiSelector instance(int instance) 找到一个拥有相同属性的对象集合中的对象,例如:UiSelector s = new UiSelector().className("android.widget.TextView").instance(1);可以找到页面层级中第二个类名为TextView的元素
UiSelector index(int index) 用法和上面的instance差不多,谷歌的原文说这个方法是unreliable的,推荐使用instance方法

通过层级关系

返回值 方法名 说明
UiSelector fromParent(UiSelector s) 获取同一个父控件的其他子控件,即获取兄弟控件
UiSelector getFromParent(UiSelector s) 获取同一个父控件的其他子控件,即获取兄弟控件
UiSelector childSelector(UiSelector s) 获取子控件
UiSelector getChild(UiSelector s) 获取子控件

使用JUnit组织测试用例

使用JUnit来组织Appium测试用例,可以添加@Before@Test或者@After等注解来使测试代码的运行变得更加灵活。

比如在开始测试之前,需要配置Capability和连接Appium服务器,而且一般需要安装应用或者打开应用的某个Activity。这时候可把这些操作放到一个方法中,并且方法添加@Before注解,则在运行的时候,@Before方法会先于@Test方法执行。

同理,测试结束后需要关闭session,回收资源等等,可以把这些操作放到一个@After方法中,@After方法在所有@Test方法结束之后执行。

显式等待和隐式等待

有时候,由于网络或者其他原因,页面跳转之后,某些元素没有立即显示出来,此时查找元素会失败。这种情况就需要引入显式等待隐式等待

线程等待

直接使用Thread.sleep();

隐式等待

driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

全局等待30秒,不管元素是否已经加载

显式等待

WebDriverWait 显示等待,显示等待时间可以通过 WebDriverWaitutil 来决定,比如这个 timeOut 是60,如果该元素60s以内出现就不在等待。

WebDriverWait wait = new WebDriverWait(driver, 60);
    WebElement e= wait.until(new  ExpectedCondition<WebElement>() {
            @Override
            public WebElement apply(WebDriver d) {
                return d.findElement(By.id("q"));
            }
        })
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 前言:本篇文章并非我的原创,而是翻译自英文原著。这里的每一句话我都仔细揣摩过并且加以实践,着实花了一些时间,在此记...
    顾顾314阅读 21,708评论 0 19
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 前言 做Android端功能自动化已有2年多的时间了,使用过的功能自动化框架有Robotium、Uiautomat...
    海波笔记阅读 16,951评论 3 66
  • 第一个游戏,把身上的贴纸甩下来,棉质衣服不好甩,最后卡卡用手扯掉然后说,这个游戏不好玩 妈妈画画孩子猜,我画了汉堡...
    山青爱自我探索阅读 173评论 0 0