1.Xpath定位 (XML path Language)
- 语法
表达式 | 结果 |
nodename | 标签名,选取此节点的所有子节点 |
/ | 从根节点选取,只能定位到他的孩子元素,至于孙子以及后面的元素就需要//来定位 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑他们的位置 |
. | 选取当前节点 |
.. | 选取当前节点的父亲节点 |
@ | 选取属性 |
* | 选取任意元素 |
- 示例:
$x('//*[@id="kw"]') | |
$x('//*[@id="s_tab"]//a[last()-1]') | 查找父元素id是 s_tab的,子元素是a标签的(last)倒数第二个 |
$x('//*[@id="s_tab"]//a[1]') | 查找父元素id是 s_tab的,a标签第一个 |
-
需要特别注意/和//的区别
-
xpath可以通过索引获取对应元素,原理同css的nth-child方法
高级用法
表达式 | 含义 |
---|---|
[last()] | 选取最后一个 |
[@属性名=‘属性值’ and @属性名=‘属性值’] | 与关系 |
[@属性名=‘属性值’ or @属性名=‘属性值’] | 或关系 |
[text()=‘文本信息’] | 根据文本进行定位 |
[contains(text(),‘文本信息’)] | 根据文本信息包含内容进行定位 |
- contains的第一个参数是匹配方式,可以选择text(),@id等,第二个参数是匹配内容,可以模糊匹配
- 定位中text()和@text的区别:
- text() 方法是定位标签中的文本信息,例如
<tr>文本信息</tr>
- @text是定位标签的text属性
<tr text='文本信息'></tr>
2. CSS定位(CSS selector)
1. 基础语法
类型 | 表达式 |
---|---|
标签 | 标签名 |
类 | .class属性值 |
ID | # id属性值 |
属性 | [属性名=‘属性值’] |
- 注意:如果一个属性的值不只是一个单词,而是用空格隔开的多个单词,用css定位查找的时候需要将空格替换成 点
2. 关系定位
类型 | 表达式 |
---|---|
并集 | 元素,元素 |
父子 | 元素>元素 |
后代 | 元素 元素 |
-
并集只要满足其中一个查找条件就认为是查找成功
-
:nth-child()找该元素爸爸的第几个孩子,索引从1开始
-
如果要指定标签类型,不能直接在nth-child()前面加类型,需要换成nth-of-type()
*示例: *
表达式 | 含义 |
---|---|
$(‘#s_tab b’) | 查找id=s_tab元素下的子孙元素b |
$(‘#s_tab a:nth-child(2)’) | a的父元素的第二个孩子标签 |
$(‘#s_tab a:nth-last-child(1)’) | 定位到最后一个元素 |
$(‘[name=wd]’) | 查找name=wd的元素 |
3. 面试题
1. 如何通过子元素定位父元素
- css和其他定位方式都无法做到,所以要使用xpath定位
- 先定位到子元素,再通过/… 可定位到父元素
2. NoSuchElementException问题如何解决
原因1. 定位语法错误
解决方法:编写正确的元素定位语句
原因2. 元素还没有完全加载出来,就去定位,所以导致NoSuchElement的问题
解决方法:添加强制等待或隐式等待,如果不再出现报错,可更换成显示等待
原因3. id通常是唯一且不会变化的,但是因为前端技术栈的发展,很多ID都是动态生成的,且一在变化
解决方法:不使用ID定位,切换更稳定的定位方法,例如CSS或Xpath
class TestDemo:
def setup(self):
self.driver = webdriver.Chrome()
def test_locate(self):
self.driver.get("https://vip.ceshiren.com/#/ui_study")
self.driver.find_element(By.XPATH,"//*[text()='动态id']").click()
time.sleep(10)
self.driver.find_element(By.XPATH,"//*[text()='动态id_1']").click()
def teardown(self):
self.driver.quit()
原因4.页面有iframe(iframe是文档中的文档)
解决方案:需要切换到对应的iframe之后再完成操作
- 可直接在页面ctrl+f,搜索iframe或frame关键字查出页面是否包含iframe
class TestDemo:
def setup(self):
self.driver = webdriver.Chrome()
def test_locate(self):
self.driver.get("https://vip.ceshiren.com/#/ui_study")
frame = self.driver.find_element(By.CSS_SELECTOR,".iframe>.iframe")
self.driver.switch_to.frame(frame)
time.sleep(2)
self.driver.find_element(By.ID,"frame_btn").click()
def teardown(self):
self.driver.quit()
原因5. 当发生打开新窗口并有页面切换的时候,报错NoSuchElement
解决方案: 那么需要在定位新窗口元素之前,切换到窗口,再进行定位
解决步骤:1. 进入页面2. 打开新窗口 3. 定位按钮
class TestDemo:
def setup(self):
self.driver = webdriver.Chrome()
def test_locate(self):
self.driver.get("https://vip.ceshiren.com/#/ui_study")
self.driver.find_element(By.ID,"openWindows").click()
# 获取倒数第一个页面,即新打开的页面
win = self.driver.window_handles[-1]
# 跳转页面
self.driver.switch_to.window(win)
self.driver.find_element(By.ID,"window_btn").click()
time.sleep(2)
def teardown(self):
self.driver.quit()
原因6. 元素是隐藏状态的时候,如果进行点击或者操作,就会报NoSuchElement错误
解决方案:隐藏按钮不能直接点击,如果要点击,必须使用js进行操作,并且document.qureySelector()函数里面只能使用css定位
class TestDemo:
def setup(self):
self.driver = webdriver.Chrome()
def test_locate(self):
self.driver.get("https://vip.ceshiren.com/#/ui_study")
self.driver.execute_script('document.querySelector(".el-button--info").click()')
time.sleep(2)
def test_php(self):
self.driver.get("https://www.php.cn/")
# 隐藏元素需要用js语句定位 #定位到第一个dl标签 # 定位到第一个a标签
self.driver.execute_script('document.querySelector("#No0 dl:nth-child(1) a:nth-child(1)").click()')
# js语句定位时,需要把进行的操作放在函数引号里面,click,send_keys之类的
time.sleep(2)
def teardown(self):
self.driver.quit()
- 辨别元素的隐藏状态:style="display: none"或者hidden=“hidden”
另外
本人还遇到过一种情况,输入框需要点击一次才可以输入,但是点击过后,元素属性发生了一些变化,这个时候就需要切换定位语句,具体要看测试页面的dom情况。
元素操作相关问题
-
明明定位到了元素,但是点击无效(也没有报错)
原因:异步加载js导致点击不到
解决方法:使用显示等待循环点击,知道生效为止 -
如何处理弹窗
原因:弹窗会遮盖元素,导致自动化脚本执行失败
解决方法,通过添加黑名单处理或switch_to.alert()处理
参考代码:
# 弹窗黑名单,将弹窗异常处理封装成装饰器
# 被装饰方法在执行前会将方法传入装饰器,先去执行装饰器
import logging
from appium.webdriver.common.mobileby import MobileBy
black_list = [(MobileBy.ID,"com.xueqiu.android:id/iv_close")]
def black_wrapper(fun):
# fun是被装饰的方法,在执行被装饰方法前会将方法作为对象传入装饰器内部执行逻辑,然后再去执行被装饰方法
def run(*args,**kwargs):
# 注意:这里需要添加代码,用来接收self对象,被装饰方法的第一个参数是self,下面需要用到实例self去定位
try:
return fun(*args,**kwargs)
except Exception as e:
logging.warning("未找到元素,抓取截图")
for black in black_list:
logging.info(f"处理黑名单:{black}")
# 将黑名单的元素解包并去定位,如果能找到就去点击,如果找不到就继续遍历,直到遍历整个黑名单列表,抛出异常
ele = basepage.driver.find_element(*black)
if ele: # 如果不为空,则证明定位到了元素,去点击并返回fun
logging.info(f"点击黑名单弹框,退出")
ele.click()
return fun(*args,**kwargs)
logging.error(f"遍历黑名单后,未找到元素,异常信息 ====>{e}")
raise e
return run
- 该段代码是做app自动化时所学,不过逻辑与web自动化的黑名单处理近似,可加以改进
判断元素是否在页面存在
- 解决方案:可以通过查看当前页面dom,搜索该元素是否存在,如果是脚本执行过程中,可以打印page_source,即可了解到该元素在运行中是否存在
文章内容输出来源:霍格沃兹测试开发学社-测开就业班四期
推荐阅读链接:面试 | 今日头条测试开发岗位面试题目回顾_自动化测试,h5和原生如何切换_霍格沃兹测试开发学社的博客-CSDN博客