Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
UI自动化
-> UI自动化第2篇:元素定位专题
1、UI自动化第1篇:环境搭建与简单示例
2、UI自动化第2篇:元素定位专题
3、UI自动化第3篇:元素操作专题
上一篇:UI自动化第1篇:环境搭建与简单示例
下一篇:UI自动化第3篇:元素操作专题
<div style="display:none"></div> ## 目录 [TOC] ## 1. 前言 UI自动化的学习,个人认为应该分五步走:环境搭建、元素定位、特殊场景处理、框架设计与搭建、测试平台开发。第一步的环境搭建其实没什么难度,都是固定的套路。今天就来到了第二步的元素定位,可以说元素定位是整个UI自动化的基本功。 我查阅了大量的资料,在动手实践的基础上,整理总结了此文。 ## 2. 常用定位方式 众所周知,Selenium提供了8种定位方式: - **id**:根据id定位,是最常用的定位方式,因为id具有唯一性,定位准确快捷 - **name**:通过元素的【名称】属性定位,name会存在不唯一的情况 - **className**:class 属性定义了元素的类名 - **tagName**:通过标签命定位,一般不建议使用 - **linkText**:专用于定位超链接元素(即a标签),需要完全匹配超链接的内容 - **partialLinkText**:同样用于定位超链接元素,但可以模糊匹配超链接的内容 - **xpath**:根据元素路径进行定位,分为绝对路径和相对路径 - **cssSelector**:selenium官方推荐的元素定位方式,比xpath效率更高,但需要掌握一些css基础 下面以百度搜索框为例,进行定位方式的实践练习 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/203/9d4c2834-bf42-43df-85ef-8ff00b4ff96a.png) 在Java中,selenium封装了获取元素的两个函数,区别在于前者会获得一个元素,后者获取一系列(1个或多个)元素的集合: ```java // 获取某个元素 WebElement findElement(By var1); // 获取元素的集合 List<WebElement> findElements(By var1); ``` ## 3. id定位 ```java import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; public class ElementTest { public static void main(String[] args) { // 指定浏览器驱动的路径 String driverPath = "E:/source/driver/chromedriver_80_2.exe"; System.setProperty("webdriver.chrome.driver", driverPath); // 创建一个chrome driver WebDriver driver = new ChromeDriver(); // 访问百度 driver.get("https://www.baidu.com"); // id定位元素 try { driver.findElement(By.id("kw")).sendKeys("测试"); Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } finally { driver.quit(); } } } ``` ## 4. name定位 根据元素的name标签定位元素,name属性的值是可重复的。 元素定位之外的相同代码省略,下同。 ```java // name定位元素 try { driver.findElement(By.name("wd")).sendKeys("测试"); Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } finally { driver.quit(); } ``` ## 5. className 很多人可能对前端的class这个概念缺乏了解,这里简单描述一下。class属性一般是对元素进行样式描述,它有两种定义方式: - 定义在HTML文件的【head】标签的【style】标签内 - 定义在专门的css文件中,用【link】标签对该css文件进行引用 一个元素可以引用多个class,一个class也可以被多个元素引用,见下面示例代码: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>测试页面</title> <link rel="stylesheet" href="../../layui/css/layui.css"> <style> .colkey { font-size: 15px; font-weight: 600; text-align: left; width: 75px; padding: 9px 0 9px 10px; } .content { margin: 0 10px 5px 25px } </style> </head> <body> <div class="colkey">测试1</div> <div class="colkey content">测试2</div> <div class="content">测试3</div> </body> </html> ``` 关于class的知识,感兴趣的可以自己去多了解前端相关内容。使用className去定位元素,其实并不是非常好的一种定位方式,原因是一个className可能被多个元素所拥有,难以保证元素定位的唯一性。 ```java // className定位元素 try { driver.findElement(By.className("s_ipt")).sendKeys("测试"); Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } finally { driver.quit(); } ``` ## 6. tagName定位 所谓tag就是元素的标签,比如head、form、div等等,通过tagName去定位元素更加难以保证定位的唯一性。 在定位百度搜索框这个案例中,有两种思路,具体见代码: ```java // tagName定位元素 try { // 思路1:获取所有input标签的元素,再根据索引获取目标元素 driver.findElements(By.tagName("input")).get(7).sendKeys("测试"); // 思路2:先定位到父级元素,再通过tagName定位目标元素 driver.findElement(By.className("s_ipt_wr")).findElement(By.tagName("input")).sendKeys("测试"); Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } finally { driver.quit(); } ``` ## 7. linkText定位 linkText定位专用于超链接元素,即a标签包裹的元素,例如: ```html <a href="http://www.w3school.com.cn">W3School</a> ``` 上面就是一个典型a标签元素,href指向目标url,"W3School"是该元素的value,我们可以用该内容定位这个a标签元素。 下面以百度搜索csdn并跳转csdn官网为例,演示linkText定位: ```java // tagName定位元素 try { driver.findElement(By.id("kw")).sendKeys("csdn"); driver.findElement(By.id("su")).click(); // 隐式等待页面加载完成 driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS); // 精确匹配 driver.findElement(By.linkText("CSDN博客")).click(); // 模糊匹配 driver.findElement(By.partialLinkText("IT技术社区")).click(); Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } finally { driver.quit(); } ``` 上面的6种定位方式,使用起来很简单,但缺陷在于很多元素用这些方式无法定位,接下来就是两种几乎能定位到所有元素的定位方式:**xpath和css selector**。这两种方式内容非常多,又比较深奥,如果有人不想努力了,倒是有偷懒方法,在目标元素上右键,`Copy selector`(获取css selector表达式)和`Copy XPaht`(获取xpath表达式): ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/203/d3f8f684-942a-4ba2-bc0f-7131f6a48633.png) ## 8. xpath定位 所谓xpath,即根据元素的路径进行定位。更多xpath的知识请见:[w3school](https://www.w3school.com.cn/xpath/index.asp) ### 8.1. 路径匹配 xpath定位最常用的就是路径定位了,具体又分为绝对路径和相对路径。 路径匹配有以下几个符号: - 用`/`表示节点路径,如`/A/B/C`表示节点A的子节点B的子节点`C`,`/`表示根节点。 - 用`//`表示所有路径以`//`后指定的子路径结尾的元素,如`//D`表示所有的D元素;如果是`//C/D`表示所有父节点为`C`的`D`元素。 - 用`*`表示路径的通配符,如`/A/B/C/*`表示`A`元素下的`B`元素下的`C`元素下的所有子元素。 #### 8.1.1 绝对路径 绝对路径也称全路径,是指从根路径出发,逐层定位,例如: ```java By.xpath("html/body/div/form/span/input") ``` 以上面的百度搜索框为例,绝对路径: ```java By.xpath("/html/body/div[1]/div[1]/div[5]/div/div/form/span[1]/input") ``` 绝对路径繁琐冗长,而且极易受前端结构变动影响,所以强烈不推荐使用。 #### 8.1.2 相对路径 即相对于上下文节点的路径,使用双斜杠,例如: ```java By.xpath("//input//div") ``` 相对路径更加实用,一般我们难以直接定位到一个目标元素时,可以先定位到一个能准确定位到的上级元素,再从此上级元素出发,通过元素之间的层级关系定位到目标元素。 例如,定位百度搜索框(当然百度搜索框本身是能定位到的,这里是为了演示): ```java By.xpath("//*[@id='form']/span/input") ``` 当一个元素下有多个同类型元素时,仅凭路径匹配就行不通了,又因为对于每一个元素,它的各个子元素都是有序的,所以**通过索引**就能准确定位到目标元素: - `/A/B/C[1]`表示`A`元素下的`B`元素下的`C`元素下的第一个子元素。 - `/A/B/C[last()]`表示`A`元素下的`B`元素下的`C`元素下最后一个子元素。 - `/A/B/C[position()>2]`表示`A`元素下的`B`元素下的`C`元素下的位置号大于2的元素。 例如: ```java By.xpath("//form[2]") ``` 通过相对路径定位元素,其核心思想在于,当目标元素不能直接定位时,先找到一个能直接定位到的元素,我称之为锚点元素,再通过目标元素与锚点元素之间的位置关系进行定位。 具体又可分为,通过上下级节点(或父子节点)和同级节点(或兄弟节点)两种方式。 示例代码: ```html <html> <body> <div id="parent"> <div id="A"> old brother</div> <div id="B"> child</div> <div id="C"> litter brother</div> </div> </body> </html> ``` 以上面代码为例: **1、通过父级节点查找子级节点** ```java By.xpath("//div[@id='parent']/div[2]") ``` **2、通过子级节点查找父级节点** ```java By.xpath("//div[@id='B']/..") ``` **3、通过兄弟节点定位** ```java By.xpath("//div[@id='B']/../div[1]") ``` 另外根据兄弟节点的相对位置关系进行定位,其他的常用表达式: - `E/following-sibling::F`:获取和`E`元素同级且位于其后的`F`元素 - `E/following-sibling::F[n]`:获取和`E`元素同级且位于其后的第n个`F`元素 - `preceding-sibling::F`:获取和`E`元素同级且位于其前的`F`元素 - `preceding-sibling::F[n]`:获取和`E`元素同级且位于其前的第n个`F`元素 下面用一个示例进行演示: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/203/33f761ae-c794-4786-9822-da75da6f94d2.png) 页面代码如下,可以发现目标元素所属的`tbody`标签只有一个动态id,显然难以直接定位。但在目标元素所在的tbody上面,有一个可以通过`id`直接定位到的`tbody`,我称之为锚点元素。锚点元素和目标元素所在tbody在同级,这时候就很适合用兄弟元素的方式去定位。 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/203/65484b20-a4e9-44cc-b00d-cfa816b9d19f.png) 具体代码如下: ```java By.xpath("//*[@id='separatorline']/following-sibling::tbody[2]/tr/th/a[3]")).click(); 1 ``` ### 8.2. 属性匹配 在xpath中可以使用属性和属性的值来定位元素,使用属性定位时要以`@`开头(下面`form`仅为示例,也可以为`div`、`input`等) - `//form[@id]`:表示所有具有属性`id`的`form`元素。 - `//form[@*]`:表示所有具有属性的`form`元素。 - `//form[not(@*)]`:表示所有不具有属性的`form`元素。 - `//form[@id="myId"]`:表示`id`值为`myId`的`form`元素。 另外还有属性模糊匹配方法,功能更加强大: - `//form[start-with(@id,'myId')]`:表示所有属性`id`的值以`myId`开头的`form`元素。 - `//form[ends-with(@id,'myId')]`:表示所有属性`id`的值以`myId`结尾的`form`元素。 - `//input[contains(@id,'myId')]`:表示所有属性`id`的值包含`myId`的`form`元素。 - `//a[contains(text(),'贴吧')]`:表示超链接元素的文本内容为贴吧的`a`元素 在实际定位中,常常是上面三种方式结合起来进行定位。 当然,另外还有使用布尔逻辑计算定位,例如: ``` By.xpath("//div[@id='myId' or @name='myName']") 1 ``` 双条件同时过滤,例如: ``` By.xpath("//div[@id='myId'][@name='myName'") 1 ``` ## 9. cssSelector定位 更详细内容请见:[W3C](https://www.w3.org/TR/selectors-3/#attribute-selectors) ### 9.1. css元素选择器 **选择`input`类型的元素:** ``` By.cssSelector("input") ``` ### 9.2. css类与id选择器 id选择器以 `#` 来定义,class类选择器以一个`.`显示,有以下几种例子: 1. 选择`id`为`myId`的元素: ```java By.cssSelector("#myId") ``` 2. 选择`id`为`myId`的`input`元素: ```java By.cssSelector("input#myId") ``` 3. 选择class为a的元素: ```java By.cssSelector(".a") ``` 4. 选择class为a、b的元素: ```java By.cssSelector(".a.b") ``` 5. 选择class为a的input元素: ```java By.cssSelector("input.a") ``` ### 9.2. css属性选择器 1. 选择有属性有属性`maxlength`的`input`元素 ```java By.cssSelector(“input[maxlength]”) ``` 2. 属性`maxlength`的值精确等于`255`的`input`元素(完全相等,区分大小写) ```java By.cssSelector(“input[maxlength=‘255’]”) ``` 3. 属性`class`的值以空格隔开,其中一项等于`fm`(区分大小写)`input`元素: ```java By.cssSelector(“input[class~=‘fm’]”) ``` 4. 属性`class`的值以`bar`开头的`div`元素 ```java By.cssSelector(“div[class^=‘bar’]”) ``` 5. 属性`class`的值以`bar`结尾的`div`元素 ```java By.cssSelector(“div[class$=‘bar’]”) ``` 6. 属性`name`的值包含`myName`的`form`元素 ```java By.cssSelector(“form[name*=‘myName’]”) ``` 7. 属性`class`有一个以“soutu”开头的用连字符分隔的值列表(从左边)的`span`元素:`<span class="soutu-btn"></span>` ```java By.cssSelector(“span[class|=‘soutu’]”) ``` 8. 属性`type`等于`hidden`且属性`name`等于`ch`且属性`class`为`bg`的`input`元素 ``` By.cssSelector("input[type='hidden'][name='ch'].bg") 1 ``` ### 9.3. css子元素选择器 何为子元素?以下面代码中的`form`元素为基准,`span`元素和`a`元素是它的子元素,但`input`元素不是。 代码示例2: ```html <form id="myForm" class="fm"> <span class="bg s_ipt_wr quickdelete-wrap"> <input id="kw" name="wd" class="s_ipt" value="" maxlength="255"> </span> <span class="bg s_ipt_wr quickdelete-wrap"> <a href="javascript:;" id="quickdelete" title="清空" class="quickdelete"></a> </form> ``` 子元素的标志符号是>,下面是几个示例。 1. `form`元素的子元素中的`span`元素: ```java By.cssSelector("form#myForm > span") ``` 2. 属性`id`为`form`的`form`元素的子元素`span`元素的子元素`input`元素(实现百度搜索框定位): ```java By.cssSelector("form#form > span > input") ``` ### 9.4. css后代元素定位 后代元素与子元素的区别是,A元素的子元素的子元素,也是A的后代元素。同样以前文代码2中的`form`元素为基准,`span`元素、`a`元素和`input`元素都是它的后代元素。 后代元素的标志符号是空格,例如同样定位百度搜索框,用后代元素方式: ```java By.cssSelector("form#form input[name='wd']") ``` ### 9.5. css相邻兄弟选择器 相邻兄弟选择器(Adjacent sibling selector)可选择紧接在另一元素后的元素,且二者有相同父元素。例如,属性`id`为`myId`的form元素的后代`div`元素的`span`子元素的相邻的第一个弟弟元素`input`: ```java By.cssSelector("form#myId div>span+input") ``` ### 9.6. css伪类选择器 这种选择器,要求目标元素必须有父级元素,且符合位置匹配条件,具体如下: - E:nth-child(n)和E:nth-last-child(n):两者的区别是前者正序计数,后者倒序计数。其次,这两个选择器定位的元素要求必须在某个父级标签内,且其父级标签内对应索引n的元素的类型必须为E,否则匹配失败。以百度搜索框代码为例,span:nth-child(7)这样是匹配失败的,因为form元素内第7个子元素是input类型元素,不是span类型。 - E:nth-of-type(n)和E:nth-last-of-type(n):两者也是正序和倒序的区别。E:nth-of-type(n)与E:nth-child(n)的区别在于,前者匹配第n个E元素,后者匹配到第n个元素并判断是否是E元素,不是则匹配失败。 几个示例如下: 1. 属性`class`为`s_ipt_wr`的`span`元素的第2个子元素,且其类型为`input`的元素(位置和类型不对应则匹配失败): ```java By.cssSelector(“span[class~=‘s_ipt_wr’] > input:nth-child(2)”) ``` 2. 属性`class`为`s_ipt_wr`的`span`元素的倒数第3个子元素,且其类型为`input`的元素(位置和类型不对应则匹配失败) ```java By.cssSelector(“span[class~=‘s_ipt_wr’] > input:nth-last-child(3)”) ``` 3. 属性`class`为`s_ipt_wr`的`span`元素的第2个`input`元素(该`input`元素在所有子元素排第几无所谓): ```java By.cssSelector(“span[class~=‘s_ipt_wr’] > input:nth-of-type(2)”) ``` 4. 属性`class`为`s_ipt_wr`的`span`元素的倒数第3个`input`元素(该`input`元素在所有子元素排第几无所谓): ```java By.cssSelector(“span[class~=‘s_ipt_wr’] > input:nth-of-type(3)”) ``` 5. 属性`class`为`s_ipt_wr`的`span`元素的子元素中排在第一且为`input`类型的元素: ```java By.cssSelector(“span[class~=‘s_ipt_wr’] > input:first-child”) ``` 6. 属性`class`为`s_ipt_wr`的`span`元素的子元素中排在最后一个且为`input`类型的元素: ```java By.cssSelector(“span[class~=‘s_ipt_wr’] > input:last-child”) ``` 7. 属性`class`为`s_ipt_wr`的`span`元素的第一个`input`类型的元素(该`input`元素在所有子元素排第几无所谓): ```java By.cssSelector(“span[class~=‘s_ipt_wr’] > input:first-of-type”) ``` 8. 属性`class`为`s_ipt_wr`的`span`元素的最后一个`input`类型的元素(该`input`元素在所有子元素排第几无所谓): ```java By.cssSelector(“span[class~=‘s_ipt_wr’] > input:last-of-type”) ``` 基本上常用的css selector定位方式都在下表中了: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/203/455c0627-15c1-4880-839a-0e11ff338f01.png) > 来源:`https://blog.csdn.net/mu_wind/article/details/105594500` <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container