对象(元素)的定位和操作是自动化测试的核心部分,其中操作又是建立在定位的基础上的,因此元
素定位就显得非常重要。 (本书中用到的对象与元素同为一个事物)
一个对象就像是一个人,他会有各种的特征(属性) ,如比我们可以通过一个人的身份证号、姓名或
者他的住址找到这个人。那么一个元素也有类似的属性,我们可以通过这种唯一区别于其它元素的属性来
定位这个元素。当然,除了要操作元素时需要定位元素外,有时候我们只是为了获得元素的属性(class 属
性,name 属性) 、text 或数量也需要定位元素。
webdriver 提供了一系列的元素定位方法,常用的有以下几种
id
name
class name
tag name
link text
partial link text
xpath
css selector
分别对应 python webdriver 中的方法为:
find_element_by_id()
find_element_by_name()
find_element_by_class_name()
find_element_by_tag_name()
find_element_by_link_text()
find_element_by_partial_link_text()
find_element_by_xpath()
find_element_by_css_selector()
2.1 id 和 name 定位
id 和 name 是我们最常用的定位方式,因为大多数元素都有这两个属性,而且在对控件的 id 和 name
命名时一般使其有意义也会取不同的名字。通过这两个属性使我们找一个页面上的属性变得相当容易。
<input id="gs_htif0" class="gsfi" aria-hidden="true" dir="ltr">
<input type="submit" name="btnK" jsaction="sf.chk" value="Google 搜索">
<input type="submit" name="btnI" jsaction="sf.lck" value=" 手气不错 ">
通过元素中所带的 id 和 name 属性对元素进行定位:
id=”gs_htif0”
find_element_by_id("gs_htif0")
name=”btnK”
find_element_by_name("btnK")
name=”btnI”
find_element_by_name("btnI")
2.2 tag name 和 class name 定位
不是所有的前端开发人员都喜欢为每一个元素添加 id 和 name 两个属性,但除此之外你一定发现了一
个元素不单单只有 id 和 name,它还有 class 属性;而且每个元素都会有标签。
<div id="searchform" class="jhp_big" style="margin-top:-2px">
<form id="tsf" onsubmit="return name="f" method="GET" action="/search">
<input id="kw" class="s_ipt" type="text" name="wd" autocomplete="off">
通过元素中带的 class 属性对元素进行定位:
class=”jhp_big”
find_element_by_class_name("jhp_big")
class=”s_ipt”
find_element_by_class_name("s_ipt")
通过 tag 标签名对对元素进行定位:
<div>
find_element_by_tag_name("div")
<form>
find_element_by_tag_name("form")
<input>
find_element_by_tag_name("input")
tag name 定位应该是所有定位方式中最不靠谱的一种了,因为在一个页面中具有相同 tag name 的元
素极其容易出现。
2.3 link text 与 partial link text 定位
有时候需要操作的元素是一个文字链接,那么我们可以通过 link text 或 partial link text 进行元素
定位。
<a href="http://news.baidu.com" name="tj_news">新 闻</a>
<a href="http://tieba.baidu.com" name="tj_tieba">贴 吧</a>
<a href="http://zhidao.baidu.com" name="tj_zhidao">一个很长的文字连接</a>
通过 link text 定位元素:
find_element_by_link_text("新 闻")
find_element_by_link_text("贴 吧")
find_element_by_link_text("一个很长的文字连接")
通 partial link text 也可以定位到上面几个元素:
find_element_by_partial_link_text("新")
find_element_by_partial_link_text("吧")
find_element_by_partial_link_text("一个很长的")
当一个文字连接很长时,我们可以只取其中的一部分,只要取的部分可以唯一标识元素。一般一个页
面上不会出现相同的文件链接,通过文字链接来定位元素也是一种简单有效的定位方式。
下面介绍 xpath 与 CSS 定位相比上面介绍的方式来说比较难理解,但他们的灵活性与定位能力比上
面的方式要强大。
2.4 XPath 定位
XPath 是一种在 XML 文档中定位元素的语言。因为 HTML 可以看做 XML 的一种实现,所以 selenium 用
户可是使用这种强大语言在 web 应用中定位元素。
XPath 扩展了上面 id 和 name 定位方式,提供了很多种可能性,比如定位页面上的第三个多选框。
<html class="w3c">
<body>
<div class="page-wrap">
<div id="hd" name="q">
<form target="_self" action="http://www.so.com/s">
<span id="input-container">
<input id="input" type="text" x-webkit-speech="" autocomplete="off"
suggestwidth="501px" >
我们看到的是一个有层级关系页面,下面我看看如果用 xpath 来定位最后一个元素。
用绝对路径定位:
find_element_by_xpath("/html/body/div[2]/form/span/input")
当我们所要定位的元素很难找到合适的方式时,都可以通这种绝对路径的方式位,缺点是当元素在很
多级目录下时,我们不得不要写很长的路径,而且这种方式难以阅读和维护。
相对路径定位:
find_element_by_xpath("//input[@id=’input’]") #通过自身的 id 属性定位
find_element_by_xpath("//span[@id=’input-container’]/input") #通过上一级目录的id属性定位
find_element_by_xpath("//div[@id=’hd’]/form/span/input") #通过上三级目录的 id 属性定位
find_element_by_xpath("//div[@name=’q’]/form/span/input")#通过上三级目录的 name 属性定位
通过上面的例子,我们可以看到 XPath 的定位方式非常灵活和强大的,而且 XPath 可以做布尔逻辑运
算,例如://div[@id=’hd’ or @name=’q’]
当然,它的缺陷也非常明显:1、性能差,定位元素的性能要比其它大多数方式差;2、不够健壮,XPath
会随着页面元素布局的改变而改变;3. 兼容性不好,在不同的浏览器下对 XPath 的实现是不一样的。
通过我们第一章中介绍的 firebug 的 HTML 和 firePath 可以非常方便的通过 XPath 方式对页面元素进
行定位。
打开 firefox 浏览器的 firebug 插件,点击插件左上角的鼠标箭头,再点击页面上的元素,firebug
插件的 HTML 标签页将看到页面代码,鼠标移动到元素的标签上(如图图3.1,<imput>)将显示当前元素的
绝对路径。
图3.1
/html/body/div[5]/div/div/from/input
或者直接在元素上右击弹出快捷菜单,选择 Copy XPath,将当前元素的 XPath 路径拷贝要脚本
本中(如图3.2) 。
图3.2
firePath 工具的使用就更加方便和快捷了,选中元素后,直接在 XPath 的输入框中显示当前元素的
XPath 的定位信息(如图3.3) 。
图3.3
3.2.5 CSS 定位
CSS(Cascading Style Sheets)是一种语言,它被用来描述 HTML 和 XML 文档的表现。CSS 使用选
择器来为页面元素绑定属性。这些选择器可以被 selenium 用作另外的定位策略。
CSS 可以比较灵活选择控件的任意属性,一般情况下定位速度要比 XPath 快,但对于初学者来说比较
难以学习使用,下面我们就详细的介绍 CSS 的语法与使用。
CSS 选择器的常见语法:
* 通用元素选择器,匹配任何元素
E 标签选择器,匹配所有使用 E 标签的元素
.info class 选择器,匹配所有 class 属性中包含 info 的元素
#footer id 选择器,匹配所有 id 属性等于 footer 的元素
E,F 多元素选择器,同时匹配所有 E 元素或 F 元素,E 和 F 之间用逗号分
隔
E F 后代元素选择器,匹配所有属于 E 元素后代的 F 元素,E 和 F 之间用
空格分隔
E > F 子元素选择器,匹配所有 E 元素的子元素 F
E + F 毗邻元素选择器,匹配紧随 E 元素之后的同级元素 F (只匹配第一
个)
E ~ F 同级元素选择器,匹配所有在 E 元素之后的同级 F 元素
E[att='val'] 属性 att 的值为 val 的 E 元素 (区分大小写)
E[att^='val'] 属性 att 的值以 val 开头的 E 元素 (区分大小写)
E[att$='val'] 属性 att 的值以 val 结尾的 E 元素 (区分大小写)
E[att*='val'] 属性 att 的值包含 val 的 E 元素 (区分大小写)
E[att1='v1'][att2*='v2'] 属性 att1 的值为 v1,att2 的值包含 v2 (区分大小写)
E:contains('xxxx') 内容中包含 xxxx 的 E 元素
E:not(s) 匹配不符合当前选择器的任何元素
例如下面一段代码:
<div class="formdiv">
<form name="fnfn">
<input name="username" type="text"></input>
<input name="password" type="text"></input>
<input name="continue" type="button"></input>
<input name="cancel" type="button"></input>
<input value="SYS123456" name="vid" type="text">
<input value="ks10cf6d6" name="cid" type="text">
</form>
<div class="subdiv">
<ul id="recordlist">
<p>Heading</p>
<li>Cat</li>
<li>Dog</li>
<li>Car</li>
<li>Goat</li>
</ul>
</div>
</div>
通过 CSS 语法进行匹配的实例:
locator 匹配
css=div
css=div.formdiv
<div class="formdiv">
css=#recordlist
css=ul#recordlist
<ul id="recordlist">
css=div.subdiv p
css=div.subdiv > ul > p
<p>Heading</p>
css=form + div <div class="subdiv">
css=p + li
css=p ~ li
二者定位到的都是 <li>Cat</li>
但是 storeCssCount 的时候,前者得到 1,后者得到 4
css=form > input[name=username] <input name="username">
css=input[name$=id][value^=SYS] <input value="SYS123456" name="vid" type="hidden">
css=input:not([name$=id][value^=SYS]) <input name="username" type="text"></input>
css=li:contains('Goa') <li>Goat</li>
css=li:not(contains('Goa')) <li>Cat</li>
css 中的结构性定位
结构性定位就是根据元素的父子、同级中位置来定位,css3标准中有定义一些结构性定位伪类如
nth-of-type,nth-child,但是使用起来语法很不好理解,这里就不做介绍了。
Selenium 中则是采用了来自 Sizzle 的 css3定位扩展,它的语法更加灵活易懂。
Sizzle Css3的结构性定位语法:
E:nth(n)
E:eq(n)
在其父元素中的 E 子元素集合中排在第 n+1 个的 E 元素 (第一个 n=0)
E:first 在其父元素中的 E 子元素集合中排在第 1 个的 E 元素
E:last 在其父元素中的 E 子元素集合中排在最后 1 个的 E 元素
E:even 在其父元素中的 E 子元素集合中排在偶数位的 E 元素 (0,2,4…)
E:odd 在其父元素中的 E 子元素集合中排在奇数的 E 元素 (1,3,5…)
E:lt(n) 在其父元素中的 E 子元素集合中排在 n 位之前的 E 元素 (n=2,则匹配 0,1)
E:gt(n) 在其父元素中的 E 子元素集合中排在 n 位之后的 E 元素 (n=2, 在匹配 3,4)
E:only-child 父元素的唯一一个子元素且标签为 E
E:empty 不包含任何子元素的 E 元素,注意,文本节点也被看作子元素
例如下面一段代码:
<div class="subdiv">
<ul id="recordlist">
<p>Heading</p>
<li>Cat</li>
<li>Dog</li>
<li>Car</li>
<li>Goat</li>
</ul>
</div>
匹配示例:
locator 匹配
css=ul > li:nth(0) <li>Cat</li>
css=ul > *:nth(0)
css=ul > :nth(0)
<p>Heading</p>
css=ul > li:first <li>Cat</li>
css=ul > :first <p>Heading</p>
css=ul > *:last
css=ul > li:last
<li>Goat</li>
css=ul > li:even Cat, Car
css=ul > li:odd Dog, Goat
css=ul > :even <p>Heading</p>
css=ul > p:odd [error] not found
css=ul > li:lt(2) <li>Cat</li>
css=ul > li:gt(2) <li>Goat</li>
css=ul > li:only-child
css=ul > :only-child
css=ul > *:only-child
[error] not found (ul 没有 only-child)
css=div.subdiv > :only-child <ul id="recordlist">
… … … …
</ul>
Sizzle Css3还提供一些直接选取 form 表单元素的伪类:
:input: Finds all input elements (includes textareas, selects, and buttons).
:text, :checkbox, :file, :password, :submit, :image, :reset, :button: Finds the input element
with the specified input type (:button also finds button elements).
下面是一些 XPATH 和 CSS 的类似定位功能比较(缺乏一定的严谨性) 。
定位方式 XPath CSS
标签 //div div
By id //div[@id='eleid'] div#eleid
By class //div[@class='eleclass']
//div[contains(@class,'eleclass')]
div.eleclass
By 属性 //div[@title='Move mouse here'] div[title=Move mouse here]
div[title^=Move]
div[title$=here]
div[title*=mouse]
定位子元素 //div[@id='eleid']/*
//div/h1
div#eleid >*
div#eleid >h1
定位后代元素 //div[@id='eleid']//h1 div h1
By index //li[6] li:nth(5)
By content //a[contains(.,'Issue 1164')] a:contains(Issue 1164)
通过对比,我们可以看到,CSS 定位语法比 XPath 更为简洁,定位方式更多灵活多样;不过对 CSS 理
解起来要比 XPath 较难;但不管是从性能还是定位更复杂的元素上,CSS 优于 XPath,笔者更推荐使用 CSS
定位页面元素。
关于自动化的定位问题
自动化测试的元素定位一直是困扰自动化测试新手的一个障碍,因为我们在自动化实施过程中会碰到
各式各样的对象元素。虽然 XPath 和 CSS 可以定位到复杂且比较难定位的元素,但相比较用 id 和 name 来
说增加了维护成本和学习成本,相比较来说 id/name 的定位方式更直观和可维护,有新的成员加入的自动
化时也增加了人员的学习成本。所以,测试人员在实施自动化测试时一定要做好沟通,规范前端开发人员
对元素添加 id/name 属性,或者自己有修改 HTML 代码的权限。
相关文章
- 【转】【selenium+Python WebDriver】之元素定位不到解决办法
- 转:python webdriver API 之定位一组对象
- 转:python webdriver API 之操作测试对象
- 转:python webdriver API 之浏览器多窗口处理
- 转:python webdriver API 之控制浏览器滚动条
- 转:python webdriver API 之cookie 处理
- 转:python webdriver API 之简单对象的定位
- 转:python webdriver API 之 验证码问题
- 转:python webdriver API 之调用 JavaScript
- 转:python webdriver API 之 获取对象的属性