- > brew install node # get node.js
- > npm install -g appium # get appium
- > npm install wd # get appium client
- > appium & # start appium
- > node your-appium-test.js
复制代码 Appium 需要依赖 Android SDK 编译在[url=]手机[/url]端运行的两个插件,因此需要首先安装相应的 Android SDK 版本。这里直接使用了 Android Studio 中自带的 SDK Manager。在 SDK Manager 中选择和测试机相对应的 SDK Platform 和较新的 Build-tools,如果需要使用模拟器测试还要装对应的 ARM/x86 System Image,以及 Intel HAXM Installer,用于加速 x86 虚拟机。Appium 使用 adb 来与目标机器通讯,因此对于真机和模拟器操作几乎都是相同的,如何建立模拟器在此不再赘述。
安装完成后需要在 Appium GUI 中配置 Android SDK 目录,随后选择 Android,点击 Launch 就可以启动 Appium Server。
图片
Appium Server 默认会监听 http://localhost:4723 ,用于 RPC 通讯。下面我们就可以打开熟悉的编程环境,编写 UI 测试用例了。这里使用 Python 进行编写,需要先安装 Appium 的 Python Client ,然后再 python 中使用 appium.webclient 就可以连接 Appium server了。
pip install Appium-Python-Client
使用 Appium 进行 UI 控制
根据注释修改相应属性后即可运行测试。手机需要打开 ADB 调试,执行完以下代码后,Appium 会在手机上安装 Appium Settings 和 Unlock 两个程序,随后微信会被启动。
- from appium import webdriver
- desired_caps = {}
- desired_caps['platformName'] = 'Android' #测试平台
- desired_caps['platformVersion'] = '5.1' #平台版本
- desired_caps['deviceName'] = 'm3_note' #设备名称,多设备时需区分
- desired_caps['appPackage'] = 'com.tencent.mm' #app package名
- desired_caps['appActivity'] = '.ui.LauncherUI' #app默认Activity
- dr = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) #启动Remote RPC
复制代码 Selenum Webdriver 使用了一种类似于 JS 中的 DOM 模型的方法来选择页面中的元素。dr 为当前正在活动的 activity 对象,可以使用 findElementByXXX 的方法来获取 Activity 中的元素。所有 Element 后带 s 的函数,均获得所有匹配的元素,不带 s 的函数获得第一个匹配的元素。
查询函数
1. findElement(s)ByName
在 Android 中基本没用。Android UI 没有 Name 这个属性。有说可以使用 text 值获取。但我并没有成功
2. findElement(s)ByClassName
通过类名来获取元素,用法如下:
- item_list = dr.find_elements_by_class_name("android.widget.LinearLayout")
- item_list[2].click()
复制代码 3. findElementById
通过 resource_id 来获取元素,每个 Activity 中都是唯一的,用法如下
- t = dr.find_element_by_id("com.tencent.mm:id/f7")
- t.send_keys(wechatId)
复制代码 4. findElement(s)ByAccessbiltiyId
在 Android 上 AccessbilityID 实际就是 contentDescription 。这个属性是为了方便视力受损人士使用手机所设置。开启 TTS 后系统会朗读相关控件的 contentDescription。
5. findElement(s)ByXPath
通过 XML Path 描述来寻找元素。我没有成功的获取到,可能是 XPath 写的有问题。
- s = dr.find_element_by_xpath("//android.widget.TextView[contains(@text,'搜索')]")
- s.click()
复制代码 6. findElementByAndroidUIAutomator
通过 UIAutomator 的选择器来获取元素。因为 Appium 在 Android 上实际是调用的 UIAutomator,所以可以通过 UIAutomator 的选择器来选择元素。
- el = dr.find_element_by_android_ui_automator("new UiSelector().text(\"搜索\")")
- el.click()
复制代码 操作函数
操作函数用于操作选定的元素,有很多,以下仅列举几个,更多的请查阅手册。
1.click
2.send_keys
3.clear
查询函数返回的元素对象可以像 JS 中的 dom 元素一样,继续使用查询函数来选定其子元素。用例如下。
- search = dr.find_element_by_id("com.tencent.mm:id/aqw").find_element_by_class_name("android.widget.RelativeLayout")
- search.click()
复制代码 如何确定查询规则
了解了相关的函数后,下面就应对 UI 进行定位了。如果是自己团队开发的程序,推荐让开发同学在所有的空间上都添加 resource_id 进行绝对定位。如果碰到没有谈价 resource_id 的元素,那就要使用别的办法进行定位了。
1. UI Automator Viewer
UI Automator Viewer 是 Android 官方的 UI 定位工具,位于 sdk/tools 下。运行后会打开 viewer 界面。点击获取按钮即可获取当前正在运行的 Activity 的 UI 结构。
2. AppiumDriver getPageSource
AppiumDriver(Client) 可以很方便的获得当前正在运行的 Activity 的 UI 描述,随后可根据返回的 XML 文档来寻找元素。
print dr.page_source
确定元素位置后,即可根据前述的 Find 方法来查找/选择元素
编写完整的测试代码
正确的获取元素之后便可以获取元素相关的信息,随后使用各语言常用的测试框架编写测试即可,如 Java 的 [url=]JUnit[/url],Nodejs 的 Mocha 等。
这里我使用 Appium 主要是为了模拟用户点击添加微信好友,所以完整的程序并没有使用到测试框架。相关的 UI 元素获取/操作方法供大家参考。
- # coding:utf-8
- from appium import webdriver
- from time import sleep
- def addFriend(dr, id, dryRun=False):
- succ = False
- wechatId = str(id)
- dr.find_element_by_accessibility_id(r"更多功能按钮").click()
- item_list = dr.find_elements_by_class_name("android.widget.LinearLayout")
- try:
- item_list[2].click()
- except:
- print "Error! in item list len"
- return succ
- el = dr.find_element_by_class_name("android.widget.ListView")
- item_list = el.find_elements_by_class_name("android.widget.LinearLayout")
- try:
- item_list[1].click()
- except:
- print "Error! in item list len"
- return succ
- t = dr.find_element_by_id("com.tencent.mm:id/f7")
- t.send_keys(wechatId)
- search = dr.find_element_by_id("com.tencent.mm:id/aqw").find_element_by_class_name("android.widget.RelativeLayout")
- search.click()
- try:
- freq = dr.find_element_by_id('com.tencent.mm:id/aqq')
- assert freq.text == u"操作过于频繁,请稍后再试。"
- print "Frequency too high! Sleep 300s"
- sleep(60)
- return succ
- except:
- pass
- try:
- dr.find_element_by_id('com.tencent.mm:id/a8x').click()
- addBtn = dr.find_element_by_id('com.tencent.mm:id/eu')
- if not dryRun:
- addBtn.click()
- succ = True
- print "Success Send Requests:" + wechatId
- except:
- print "No Such User Or Already a Friend:" + wechatId
- while True:
- try:
- dr.find_element_by_id('com.tencent.mm:id/fb').click()
- except:
- try:
- dr.find_element_by_id('com.tencent.mm:id/f4').click()
- except:
- break
- return True
- def resetActivity(dr, desired_caps):
- dr.start_activity(desired_caps['appPackage'], desired_caps['appActivity'])
- desired_caps = {}
- desired_caps['platformName'] = 'Android'
- desired_caps['platformVersion'] = '5.1'
- desired_caps['deviceName'] = 'm3_note'
- desired_caps['appPackage'] = 'com.tencent.mm'
- desired_caps['appActivity'] = '.ui.LauncherUI'
- print "Trying connect to phone..."
- dr = {}
- try:
- dr = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
- except Exception, e:
- print "Cannot Connect to phone :", e
- exit()
- print "Successfully connect to phone."
- print "Reading friend list..."
- friendList = []
- fp = open("friends.txt")
- line = fp.readline().strip()
- while line:
- friendList.append(line)
- line = fp.readline().strip()
- print "Finish reading friends. Total: " + str(len(friendList))
- print "Wait for Wechat's splash screen...."
- for i in range(0, 10):
- print 10 - i
- sleep(1)
- succ_list = []
- fail_list = []
- for i in friendList:
- try:
- succ = addFriend(dr, i, dryRun=False)
- if succ:
- succ_list.append(i)
- else:
- fail_list.append(i)
- except:
- fail_list.append(i)
- resetActivity(dr, desired_caps)
- print "Succeed List:"
- print "\n".join(succ_list)
- print "Failed List:"
- print "\n".join(fail_list)
- dr.close()
复制代码