其实APP自动化测试的元素定位方式和Web自动化测试元素定位方式大体相同,无论是APP还是Web自动化测试,最重要的一个环节就是获取元素的定位,只有准确的定位到了元素才能进行相关元素的操作,而Appium也提供了许多元素定位的方式:
元素定位的方式 | |||
id | name | class | List定位 |
相对定位 | Xpath定位 | H5 页面元素定位 | UIAutomator 定位 |
ID大家都很熟悉,就像我们每个人的身份证号码一样都是唯一的,在APP界面元素中同样也可以使用元素的 id 值来区分不同的元素,然后进行定位操作,找到该元素,在 Appium 中我们可以使用 find_element_by_id() 方法来进行定位。
wjd_test.py
from appium import webdriver
import time
xg_caps = {}
xg_caps['platformName'] = 'Android'
xg_caps['deviceName'] = '127.0.0.1:62001'
xg_caps['platformVersion'] = '4.4.2'
xg_caps['app'] = '/app包存放的路径/wandoujia.apk'
xg_caps['appPackage'] = 'com.wandoujia.phoenix2'
xg_caps['appActivity'] = 'com.pp.assistant.activity.PPMainActivity'
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', xg_caps)
driver.implicitly_wait(5)
jump = driver.find_element_by_id('com.wandoujia.phoenix2:id/aio')
jump.click()
问题:有的时候在启动过程中会遇到更新版本的情况,处理的方式如下:
update_test.py
from appium import webdriver
from selenium.common.exceptions import NoSuchElementException
xg_caps = {}
xg_caps['platformName'] = 'Android'
xg_caps['platformVersion'] = '4.4.2'
xg_caps['deviceName'] = '127.0.0.1:62001'
xg_caps['appPackage'] = 'com.wandoujia.phoenix2'
xg_caps['appActivity'] = 'com.pp.assistant.activity.PPMainActivity'
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', xg_caps)
driver.implicitly_wait(5)
def check_jumpBtn():
print('check_jumpBtn')
try:
jumpBtn = driver.find_element_by_id('ccom.wandoujia.phoenix2:id/v8')
except NoSuchElementException:
print('no jumpBtn')
else:
jumpBtn.click()
print('点击跳过')
def check_cancelBtn():
print('check_cancelBtn')
try:
cancelBtn = driver.find_element_by_id('com.wandoujia.phoenix2:id/s5')
except NoSuchElementException:
print('no cancelBtn')
else:
cancelBtn.click()
check_jumpBtn()
check_cancelBtn()
执行更新脚本示例图:
抽离复用代码:capability.py
// 复用代码
from appium import webdriver
from selenium.common.exceptions import NoSuchElementException
xg_caps = {}
xg_caps['platformName'] = 'Android'
xg_caps['platformVersion'] = '4.4.2'
xg_caps['deviceName'] = '127.0.0.1:62001'
xg_caps['appPackage'] = 'com.wandoujia.phoenix2'
xg_caps['appActivity'] = 'com.pp.assistant.activity.PPMainActivity'
xg_caps['noReset'] = True
// 当send_keys输入中文时,需要配置下面两项内容,让Appium的输入法守护来执行相应的输入操作
xg_caps['unicodeKeyboard'] = True
xg_caps['resetKeyboard'] = True
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', xg_caps)
driver.implicitly_wait(5)
def check_cancelBtn():
print('check_cancelBtn')
try:
cancelBtn = driver.find_element_by_id('com.wandoujia.phoenix2:id/s5')
except NoSuchElementException:
print('no cancelBtn')
else:
cancelBtn.click()
print('点击取消更新')
check_cancelBtn()
参考代码:
from capability import driver
def login():
// 点击左上角的主菜单导航按钮
driver.find_element_by_id('com.wandoujia.phoenix2:id/w4').click()
driver.implicitly_wait(2)
// 点击右上角设置按钮
driver.find_element_by_id('com.wandoujia.phoenix2:id/pp_item_setting').click()
driver.implicitly_wait(2)
// 点击登录按钮,进入登录界面
driver.find_element_by_id('com.wandoujia.phoenix2:id/ow').click()
driver.implicitly_wait(2)
// 清空用户名,并输入用户名和密码
driver.find_element_by_id('com.wandoujia.phoenix2:id/l_').clear()
driver.find_element_by_id('com.wandoujia.phoenix2:id/l_').send_keys('邮箱')
driver.find_element_by_id('com.wandoujia.phoenix2:id/la').send_keys('密码')
// 点击同意复选框
driver.find_element_by_id('com.wandoujia.phoenix2:id/mw').click()
driver.implicitly_wait(2)
// 点击登录按钮
driver.find_element_by_id('com.wandoujia.phoenix2:id/mf').click()
login()
遇到的问题:在获取主菜单导航按钮的时候,用appium的录制功能获取到的是这个按钮的父控件的id,它本身的id找了好久没有找到,之后采用uiautomatorviewer这获取到了主菜单导航按钮的id
Tip:当我们在使用完Appium的输入操作之后,有可能会遇到自带的输入法无法唤起的问题,可以在 系统设置—>语言和输入法—>当前输入法(将当前的输入法替换为系统输入法或者其他的输入法即可)
用 name 来定位的话,对于Android来说,其实就是text的属性而已。
使用方法:
from capability import *
driver.find_element_by_name('手机号/email').send_keys('邮箱')
driver.find_element_by_name('登录').click()
Tip:因为由于text的属性不是很稳定,所以在 Appium 1.5 的时候就已经开始放弃了这个方法了。(可以对比一下下面图片中红色框框中的内容就了解了)
用 className 来进行定位的话,其实就是根据元素的类型来进行定位,但是在一个APP中有很多元素的 classname 都是相同的。
注:我们可以在对照下上图中的 绿色框框中的内容,会发现 手机号/email 和 密码框 的 classname 都是 "android.widget.EditText",因为如果我们使用className 来进行定位的话,就只能定位到 手机号/email 。
使用方法:
from capability import *
driver.find_element_by_class_name('android.widget.EditText').send_keys('邮箱')
// 密码框如果也用className来定位的话,密码会直接输入到 手机号/邮箱 的框中,这种显然是错误的,正确使用的话,可以参考代码后面的提示
driver.find_element_by_class_name('android.widget.EditText').send_keys('密码')
driver.find_element_by_class_name('android.widget.TextView').click()
Tip:其实也有一个特别鸡贼的方法,那就是 手机号/email 的输入框用 name 来定位,而密码框则用 className 来进行定位。虽然说这样也确实能实现,也很鸡贼,但是如果用 id 来定位的话,就不需要考虑那么多了吧,所以呢,一般情况下我是采用 id 来进行定位滴
相对定位的意思其实就是先找该元素对应属性的父元素的节点,而后再基于父元素进行该元素的定位。
在不使用 id 元素定位的方式,在修改个人资料页面点击头像按钮。
relative.py
from capability import *
// 使用相对定位的前提是在这个父元素节点中只有这一个ImageView元素
super_celement = driver.find_element_by_id('com.wandoujia.phoenix2:id/lk')
super_celement.find_element_by_class_name('android.widget.ImageView').click()