0%

Selenium学习笔记:定位器

本文基于 Selenium4

部分内部源自 Selenium2 或 Selenium3,Selenium4 文档中未提及,未完全验证其准确性,可能存在版本差异。

核心步骤:定位

使用 Selenium 进行浏览器自动化测试的最核心步骤其实就是:定位与操作——首先要查找到页面元素,然后对其进行操作。

元素的操作一般都是固定的,比较简单。因此,查找元素才是关键。

传统的定位器

Selenium 提供了 8 种常规的定位器,实现不同的定位策略:

定位器 描述
id 定位 id 属性匹配的元素
name 定位 name 属性匹配的元素
css selector 定位 CSS 选择器匹配的元素
xpath 定位 XPath 表达式匹配的元素
class name 定位 class 属性匹配的元素(指定单个类名,不允许复合类名)
tag name 定位标签名匹配的元素
link text 定位可视文本完全匹配的链接元素
partial link text 定位可视文本包含指定值的链接元素

【说明】

所谓“传统定位器”,是指 Selenium 各版都提供的定位器。它是相对于 Selenium4 提供的新定位器——相对定位器而言的,后文会讲解。

优先级

定位器有 8 种,如何选择用哪一种呢?

除了具体问题具体分析外,还可以参考这样的优先级:

id > name > link text > class name > css > xpath > tag name

【说明】

优先级是根据定位器的健壮性进行的排序。

id定位器

id 是唯一的、最稳定的、最快速的定位方式——呃,从理论上来说。

id 有效性完全依赖于前端开发规范,前端任何不规范的编码都可能导致定位问题。

id 定位器可用的前提条件大致有以下这些:

  • 操作的关键元素定义了 id

  • 元素 id 是稳定的

  • 元素 id 是唯一的

首先,开发规范应强制开发对所需操作的元素定义 id,否则 id 定位器根本不可用。

其次,元素 id 应是稳定的。某些前端 UI 库会为组件自动生成 id,通常每次访问页面都会生成不一样的 id。这种场景下,虽然元素定义了 id,但并不能使用 id 定位器查找。

另外,开发编码问题可能导致元素 id 不唯一。这里不讨论开发为不同组件指定了相同 id 的情况(通常很少发生),而是说封装的组件层级结构中出现了多个相同 id。比如,外层 div 和内层 input 有相同的 id——这通常是编码错误——这将导致 id 查找到的总是外层 div 而无法操作内层的 input

【扩展说明】

还有一种类似场景是,封装组件只在外层 div 元素上定义了 id,内层的 input 元素反而没有 id。

name定位器

应用场景有限,因为 name 属性不具唯一性。

通常在原生的 html 表单中查找一组单选按钮或复选框时才会用到。

css定位器

应用最广的定位器之一。页面结构变更可能使 css 定位器失效,需要更新。通常,其他优先的定位器不可用的情况下,应首选 css 定位器。

需要注意的是,在跨浏览器测试中,css 定位器在不同浏览器中的行为可能不一致——在一个中起作用,而在另一个中不起作用——所以,应在被测的所有浏览器中小心调试,确保 css 定位器均有效。

【不一致的原因】

css 定位器行为不一致的原因可能有两方面:

  • 不同版本的浏览器对 css 支持程度不同

  • 老旧的浏览器版本原生不支持 CSS 选择器

【扩展说明】

对于原生不支持 CSS 选择器的浏览器——通常是老版本,比如:IE6、IE7 和 FF3.0 等——将使用 Sizzle 作为 CSS 选择器引擎,实现或有差异。

注:Sizzle 脱胎于 jQuery 的选择器引擎。

xpath定位器

应用最广的定位器之一。页面结构变更可能使 xpath 定位器失效,需要更新。通常在 css 定位器功能无法满足需求时使用。换句话说,xpath 定位器的能力大多数情况下比 css 定位器强。

【扩展说明】

XPath 本身是一门独立的 XML 文档导航语言。要使用 xpath 定位器,至少需要了解 XPath 语言。

xpath 定位器通常需要注意以下问题:

  • 跨浏览器一致性问题
  • 属性默认值匹配问题

行为一致性

与 css 定位器类似,跨浏览器测试中,也需要注意 xpath 定位器的行为一致性。

同样,有的浏览器原生不支持 XPath 查询,Selenium 会提供实现;而原生支持 XPath 查询的浏览器,XPath 引擎实现也可能存在差异。

更多说明可参考“附录”相关章节。

属性默认值

html 某些元素的属性具有默认值,因此是非必须的。比如,input 元素的 type 属性可以缺省,默认值为 text。但是,xpath 定位器不会匹配未声明的属性默认值。

class name定位器

应用场景很少。

只要元素有一个类名与指定值相同就匹配,因此通常会匹配到多个元素。

通常,除非类名具有唯一特异性,否则应使用 css 定位器代替。

【注意事项】

只能指定单个类名。元素包含多个类名,只能选择其中一个类名。

tag name定位器

应用场景很少。

同一标签通常匹配多个元素,通常需要通过其他手段进行区分,还不如使用 css 定位器代替。

一个典型应用场景是,查找的标签在页面中很“罕见”。比如 iframe 标签,整个页面一般只有少量几个,甚至可能是唯一的。

仅针对链接,并要求链接文本与指定值完全相同。

与 link text 定位器的区别仅在于,它指定的值只需要包含在链接文本中即可。这很适合链接文本会动态变化,但一部分固定不变的场景。

相对定位器

相对定位器(Relative Locator),是 Selenium4 新增的定位方式。

早期称为“友元定位器”(Friendly Locator)。

为了消除程序语言差异,以下仅描述相关知识,不涉及代码示例(如需要请参考官方文档)。

某些页面元素很难构造一个简单的传统定位器来查找,但是使用它与其他元素的位置关系来描述该元素又容易得多。相对定位器正是这样做的——我们只需要为要查找元素构造一个的简单传统定位器,再附加它与另一元素的相对位置,就定义了一个相对定位器。

【定位原理】

Selenium 使用 JS 函数 getBoundingClientRect() 获取元素大小和位置信息,依此进行相对定位。

相对定位器允许我们指定查找元素在基准元素的:上(above)、下(below)、左(left of)、右(right of)以及附近(near),甚至将两个方向上的基准元素组合成“链式相对定位器”(chaining relative locator)。

【应用场景】

near 相对定位器

它常用于相对位置不明确,或是位置随窗口大小变化的场景。

官方文档中提及了一种应用场景:带文本标签的表单输入元素。这些输入元素可能不易定位,但它关联的文本标签往往容易定位得多。

注意:查找元素与基准元素的最大距离为 50px。

below 相对定位器

一个典型的应用场景是:某些 UI 库的下拉列表组件的下拉项的查找。

现代大多数 UI 库下拉列表组件的下拉选项都是嵌套在 div 中的 ul 模拟的,并且它们很可能是动态追加到 DOM 中,收起时不从 DOM 移除仅隐藏。因此,当页面中存在多个下拉列表时,由于结构相似,下拉选项不太容易区分。但是,如果以下拉列表的文本框为基准,查找其下方的下拉选项就容易多了。

附录

XPath跨浏览器行为一致性讨论

驱动 标签与属性名 属性值 原生 XPath 支持
HtmlUnit Driver 小写 同页面出现一致
Internet Explorer Driver 小写 同页面出现一致
Firefox Driver 大小写不敏感 同页面出现一致

假设有以下页面:

1
2
<input type="text" name="example" />
<INPUT type="text" name="other" />

匹配结果:

XPath 表达式 HtmlUnit Driver Internet Explorer Driver Firefox Driver
//input 1 (example) 2 2
//INPUT 0 0 2

总结来说:

  • Firefox 对 XPath 表达式以及页面中元素标签名的大小写都不敏感,无论大小写元素均会“全部”匹配。
  • HtmlUnit 在 XPath 表达式中只认小写标签——只匹配页面的小写标签——忽略大写标签。
  • IE 在 XPath 表达式中只认小写标签——但页面中的大小写标签均会匹配——忽略大写标签。

CSS选择器获取技巧

从“开发者工具”中复制出的 CSS 选择器,它尽可能是从最近的带 id 的元素开始的。

这里存在一个问题:如果这个 id 是自动生成的“随机 id”,不能用,怎么获取“完整”的 CSS 选择器呢?

一个小技巧是,在“开发者工具”中将 id 属性删除,重新复制 CSS 选择器。

【扩展说明】

XPath 可以一样的处理。只不过对于 Chrome 浏览器而言,复制功能本身就有“复制完整的 XPath”这个选项。

对抗变更

现代主流浏览器都可以从诸如“开发者工具”中直接复制出元素的 CSS 选择器或是 XPath 路径。

但是,通常不建议直接使用复制得到的结果。因为,复制得到的结果通常是逐层列出的,对页面结构的变化相当敏感,一旦页面有少许变化就很可能会失效,进而重新获取并更新。

一个推荐的做法是,尽量截取特征比较固定的层级,而忽略易变的层级,不要逐层列举。比如定位表单中的输入控件,一般保留三层结构即可:表单根元素、组件封装根元素、输入控件元素。当然,根据实际场景可以作出相应的调整。

强大的 XPath

所有查找方式中,XPath 无疑是最强大的,没有之一。XPath 可以使用大量的运算符以及函数,实现很多 CSS 选择器无法实现的查找能力。比如在一个元素上通过 .. 可以查找该元素的父元素——CSS 选择器无法做到。

表达式 效果 说明
//x | //y 查找多个路径
//*[count(x)=n] 查找 x 元素个数为 n 的元素
//*[local-name()=’x’] 查找标签名为 x 的元素 看起来很鸡肋,跟其他函数结合起来用就很强大了
//*[starts-with(local-name(), ‘x’)] 查找标签名以 x 开头的元素
//*[contains(local-name(), ‘x’)] 查找标签名包含 x 的元素
//*[string-length(local-name())=n] 查找标签名长度为 n 的元素