霍格沃兹测试开发学社技术学习笔记之web自动化测试元素定位笔记

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的区别:
    1. text() 方法是定位标签中的文本信息,例如
    <tr>文本信息</tr>
    
    1. @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情况。

元素操作相关问题

  1. 明明定位到了元素,但是点击无效(也没有报错)
    原因:异步加载js导致点击不到
    解决方法:使用显示等待循环点击,知道生效为止

  2. 如何处理弹窗
    原因:弹窗会遮盖元素,导致自动化脚本执行失败
    解决方法,通过添加黑名单处理或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,即可了解到该元素在运行中是否存在


文章内容输出来源:霍格沃兹测试开发学社-测开就业班四期

推荐阅读链接:面试 | 今日头条测试开发岗位面试题目回顾_霍格沃兹测试开发学社的博客-CSDN博客

1 Like

不错

谢谢老师

关闭