一、前置说明
在自动化测试中,如果不能直接定位到目标元素,可以采用"层级定位"的方式来定位,包括:
- 父元素 --> 目标元素
- 瞄点元素 --> 父元素 --> 目标元素
可以使用Xpath,按这个思路封装一个层级定位的方法。
二、代码实现
from appium.webdriver.webdriver import WebDriver as Remote from appium.options.android import UiAutomator2Options from libs.check import CommonChecker from libs.decorators import rerun_if_exception class WebDriver(Remote): def __init__(self, command_executor, capabilities: dict, *args, **kwargs): options = UiAutomator2Options().load_capabilities(capabilities) super().__init__(command_executor=command_executor, options=options, *args, **kwargs) def find_elements_by_xpath_axis(self, anchor, target, parent=None): """ 轴定位原理解析://span[text() = "anchor"]/ancestor::div[@class="parent"]//a[@class="target"] 1.//span[text() = "anchor"]: 选择所有文本内容为 "anchor" 的 <span> 元素。 2./ancestor::div[@class="parent"]: 选择之前选择的 <span> 元素的祖先中 class 为 "parent" 的 <div> 元素。 3,//a[@class="target"]: 选择所有 class 为 "target" 的 <a> 元素,这些元素是之前选择的 <div> 元素的后代。 总结:这个xpath表达式的作用:找到文本为"anchor"的<span>元素,然后找到其祖先中class为"parent"的<div>元素, 最后在该<div>元素的后代中找到class为"target"的<a>元素。 xpath使用方法: 1.一般写法:标签名[@属性名='属性值'], 在web页面中"标签名"为tag_name,在移动端中"标签名"使用class_name; 或者:*[@属性名='属性值'],但不推荐这种写法,会降低定位效率; 2.如果需要多个属性定位,则使用:标签名[@属性名1='属性值'][@属性名2='属性值'][@属性名...='属性值'] 3.如果需要使用contains匹配一个属性值中包含的字符串,则使用:标签名[contains(@属性名,'属性值')] 4.如果需要使用starts-with匹配一个属性开始位置的关键字,则使用:标签名[starts-with(@属性名,'属性值')] 5.如果需要使用contains匹配一个属性值中包含的字符串,则使用:标签名[contains(@属性名,'属性值')] 6.如果需要使用text匹配文本信息,在web页面中则使用:标签名[text()='文本'],或[contains(text(),'文本')], 在app中使用:标签名[@text='文本'] 用法:通过瞄点元素,定位目标元素;或通过瞄点元素,定位最近的父元素,再由父元素定位到目标元素。 :param anchor: 瞄点元素, 请遵循xpath使用方法 :param parent: 父元素, 直接写"标签名"即可,,如果父元素有多个,使用last()可定位到离瞄点元素最近的标签; :param target: 目标元素,请遵循xpath的使用方法 :return: List[WebElement] """ xpath = f'//{anchor}//{target}' if parent: xpath = f'//{anchor}/ancestor::{parent}[last()]//{target}' return self.find_elements('xpath', xpath) def find_element_by_xpath_axis(self, anchor, target, parent=None, index=0): elements = self.find_elements_by_xpath_axis(anchor, target, parent) return CommonChecker.check_and_get_element_by_index(elements, index, field_name='index')
三、Demo验证
测试代码:
def test_find_elements_by_xpath_axis(): from driver.appium.driver import WebDriver appium_server_url = 'http://localhost:4723' capabilities = { "platformName": "Android", "automationName": "uiautomator2", "deviceName": "127.0.0.1:62001", "app": "D:\\resources\\imooc.apk", } driver = WebDriver(command_executor=appium_server_url, capabilities=capabilities) time.sleep(2) driver.swipe_to('left', num=2) time.sleep(2) driver.find_element('id', 'cn.com.open.mooc:id/viewpager').click() time.sleep(2) ele = driver.find_element_by_xpath_axis(anchor="android.widget.EditText[@id,'cn.com.open.mooc:id/et_phone_edit']", parent="android.widget.RelativeLayout", target='android.widget.TextView[contains(@text, "去登录")]', index=0) assert ele.text == '已有慕课网账号? 去登录'
控制台输出结果,验证成功:
============================= test session starts ============================= collecting ... collected 1 item test_appium.py::test_find_elements_by_xpath_axis PASSED [100%] ============================= 1 passed in 25.78s ==============================