UI test with SpecFlow and Selenium - 定位元素

Selenium-WebDriver API for locating UI elements

Selenium Webdriver提供了很多种获取UI Element的方法,By id, className, tagName, name, linkText, partialLinkText, cssSelector, xpath, Using JavaScript。参考Selenium官网 Locating UI Elements (WebElements)。

我们这里只介绍3种经常被用到的方法:id, cssSelector, xpath。

  • id: 最理想的定位。但是一个设计良好的页面,是不可能给每一个element都加上id的。

    IWebElement element = driver.findElement(By.id("coolestWidgetEvah"));
    
  • xpath: XML Path的简称。 HTML文档本身也是一个标准的XML页面,所以我们可以使用xpath来定义页面的element。

    • 非常强大的定位方法,所有的element都可以用xpath获取
    • 如果xpath包含很多绝对路径的话,一旦页面结构发生变化,测试就会失败。
    
    
    
    List inputs = driver.findElements(By.xpath("//input"));
    
  • cssSelector: 跟xpath比较类似,但是速度更快。

    • 使用xpath时采用的是遍历页面的方法,而cssSelector使用css来定位element,因此在性能上cssSelector开销更少。
    • cssSelector会比xpath稳定,原因是页面结构常变化,但是用于定义element样式的css不会经常改变,除非有大的UI变动。
    • 需要注意的是,在不同的浏览器上,同一个元素的cssSelector可能是不一样的。
    milkcheese
    IWebElement cheese = driver.findElement(By.cssSelector("#food span.dairy.aged"));

Evaluate and validate XPath/CSS selectors in Chrome Developer Tools

获取xpath和cssSelector,可以借助Chrome Developer Tooles:


UI test with SpecFlow and Selenium - 定位元素_第1张图片
图1.png

也可以借助Chrome的小工具Copy Css Selector,在Chrome Extensions搜索安装即可。


UI test with SpecFlow and Selenium - 定位元素_第2张图片
图2.png

借助工具拷贝得到的结果,往往不能直接拿来使用:

#xpath
IWebElement button = driver.findElement(By.xpath("//*[@id='login']/div/div[1]/div[1]/button"));

#cssSelector
IWebElement button = driver.findElement(By.cssSelector("#login > div:nth-child(4) > div > div > button"));

这是两个是用Developer Tooles拷贝的结果,它们都是从一个id为login的element开始遍历,一旦element结构发生变化,测试就会失败。

来看一个比较好的例子:

xpath
WebElement button = driver.findElemen(By.xpath("body[data-controller='flight'][data-action='index']"));

cssSelector
WebElement button = driver.findElement(By.cssSelector("[id$=search_from_date]"));

对比上面两个例子,我们来总结一下,好的locating有哪些特点:

  1. 不要包含过多路径,尤其是绝对路径,除非是必要的
  2. 可变因素尽量少,能用1个className定位的元素就不要用多个

验证xpath和cssSelector的有效性,Chrome Dev Tools也提供了2种方法。一种是使用Elements的search功能,在Elements中 Ctrl+F 打开搜索,输入xpath或者cssSelector,就可以查看elements的数量和位置。


UI test with SpecFlow and Selenium - 定位元素_第3张图片
图4.png

另一种方法是在Console中使用$x("some_xpath")或者$$("css-selectors")

UI test with SpecFlow and Selenium - 定位元素_第4张图片
图3.png

Usage of xpath

  • 当xpath的路径以“/”开头时,表示绝对路径,xpath解析引擎会从文档的根节点开始解析。当/出现在xpath的路径中时,表示寻找父节点的直接子节点。
  • 当xpath的路径以“//”开头时,表示相对路径,xapth解析引擎会从文档的任意符合的节点开始解析。当//出现在xpath路径中时,表示寻找父节点下任意符合条件的子节点,不管嵌套了多少层级。

xpath基于准确元素属性的定位:

表达式 描述
/bookstore 查找根元素bookstore
/bookstore/book[1] 查找根元素bookstore子元素的第一个book元素
/bookstore/input[last()-1] 查找根元素bookstore子元素的倒数第二个book元素
//form[1] 查找页面上第一个form元素
//form[1]/input 查找页面上第一个form元素内直接子input元素(即from的下一级是input的元素)
//form[1]//input 查找页面上第一个form元素内所有的input元素(不管嵌套了多少层)
//form[@id='loginForm'] 查找id为loginForm的form元素
//input[@name='username'] 查找name位username的input元素
//*[@id='login'] 查找id为login的元素(不确定类型)
//form[@id='loginForm']/input[1] 查找id为loginForm的Form元素下的第一个input元素
//input[@name='continue'][@type='button'] 查找name为continue,type为button的input元素

xpath还支持模糊匹配查询:

XPath轴(Axes)可定义相对于当前元素的元素集。

轴名称 表达式 描述
parent //*[@id="q"]/parent::div 查找当前节点父节点,等价于//*[@id="q"]/..
child //*[@id="fkbx"]/child::input 查找当前节点的子节点,等价于//*[@id="fkbx"]/input,通常可省略。
ancestor //*[@id="q"]/ancestor::div 查找当前节点的所有上层节点,只能找上层节点,不能找上层节点的兄弟节点。
descendant ///*[@id="f"]/descendant::div 查找当前节点的所有下层节点,不管嵌套多少层。
following //*[@id="hf"]/following::input[@id="q"] 查找当前节点之后显示的所有节点,包括:子节点、兄弟节点、兄弟节点的子节点。
following-sibling //*[@id="hf"]/following-sibling::div 查找当前节点之后显示的所有兄弟节点。
preceding //*[@id="spchx"]/preceding::input[@id="q"] 查找当前节点前面显示的所有节点,包括兄弟节点、兄弟节点的子节点、父节点、父节点的兄弟节点等。
preceding-sibling //*[@id="fkbx"]/preceding-sibling::div 查找当前节点前面所有的兄弟节点。
image.png

Usage of CSS Selector

  • 定位href属性为/about.html的a元素:a[href="about.html"]

  • 定位id为loginForm中的name为username, type为text的子input元素:#loginForm > input[name="username"][type="text"]

    IWebElement password = driver.findElement(By.cssSelector("#login_form > dl > dt > input.credential));
    

注意:跟xpath相比,[]内的不需要用@指明属性。这里的路径 > 不可省略。

  • 定位使用了复合样式表的元素

    
    
    driver.findElement(By.cssSelector("button.btn.btn_big.btn_submit"));
    
  • CSS Selector还有一些高级用法: “^”匹配一个前缀字符,“$”匹配一个后置字符, “*”匹配任意字符

    定位一个有id属性,并且id属性是以”id_”开头的a元素:a[id^='id_']
    定位一个有id属性,并且id属性是以”_about”结尾的a元素:a[id$='_about']
    定位一个有id属性,并且id属性中包含”pattern”字符的a元素:a[id*='id_pattern']

  • CSS Selector同样支持定位子节点、兄弟节点的选择:CSS选择器

  • 当不想包含某个class的时候, 比如:By.CssSelector("div.active:not(.hide)")

Summary

  1. 当element有id属性时,尽量使用id
  2. xpath很强大,但是定位性能不是很好,可以考虑cssSelector
  3. 当有超链接元素时,可以考虑partialText和linkText

你可能感兴趣的:(UI test with SpecFlow and Selenium - 定位元素)