【UI自動化-2】UI自動化元素定位專題

前言

UI自動化的學習,個人認爲應該分五步走:環境搭建、元素定位、特殊場景處理、框架設計與搭建、測試平臺開發。第一步的環境搭建其實沒什麼難度,都是固定的套路。今天就來到了第二步的元素定位,可以說元素定位是整個UI自動化的基本功。
我查閱了大量的資料,在動手實踐的基礎上,整理總結了此文。

常用定位方式

衆所周知,Selenium提供了8種定位方式:

  • id:根據id定位,是最常用的定位方式,因爲id具有唯一性,定位準確快捷
  • name:通過元素的【名稱】屬性定位,name會存在不唯一的情況
  • className:class 屬性定義了元素的類名
  • tagName:通過標籤命定位,一般不建議使用
  • linkText:專用於定位超鏈接元素(即a標籤),需要完全匹配超鏈接的內容
  • partialLinkText:同樣用於定位超鏈接元素,但可以模糊匹配超鏈接的內容
  • xpath:根據元素路徑進行定位,分爲絕對路徑和相對路徑
  • cssSelector:selenium官方推薦的元素定位方式,比xpath效率更高,但需要掌握一些css基礎

下面以百度搜索框爲例,進行定位方式的實踐練習
在這裏插入圖片描述
在Java中,selenium封裝了獲取元素的兩個函數,區別在於前者會獲得一個元素,後者獲取一系列(1個或多個)元素的集合:

// 獲取某個元素
WebElement findElement(By var1);
// 獲取元素的集合
List<WebElement> findElements(By var1);

1 id定位

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();
        }
    }
}

2 name定位

根據元素的name標籤定位元素,name屬性的值是可重複的。
元素定位之外的相同代碼省略,下同。

    // name定位元素
    try {
        driver.findElement(By.name("wd")).sendKeys("測試");
        Thread.sleep(5000);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        driver.quit();
    }

3 className

很多人可能對前端的class這個概念缺乏瞭解,這裏簡單描述一下。class屬性一般是對元素進行樣式描述,它有兩種定義方式:

  • 定義在HTML文件的【head】標籤的【style】標籤內
  • 定義在專門的css文件中,用【link】標籤對該css文件進行引用

一個元素可以引用多個class,一個class也可以被多個元素引用,見下面示例代碼:

<!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可能被多個元素所擁有,難以保證元素定位的唯一性。

    // className定位元素
    try {
        driver.findElement(By.className("s_ipt")).sendKeys("測試");
        Thread.sleep(5000);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        driver.quit();
    }

4 tagName定位

所謂tag就是元素的標籤,比如head、form、div等等,通過tagName去定位元素更加難以保證定位的唯一性。
在定位百度搜索框這個案例中,有兩種思路,具體見代碼:

    // 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();
    }

5 linkText定位

linkText定位專用於超鏈接元素,即a標籤包裹的元素,例如:

<a href="http://www.w3school.com.cn">W3School</a>

上面就是一個典型a標籤元素,href指向目標url,"W3School"是該元素的value,我們可以用該內容定位這個a標籤元素。

下面以百度搜索csdn並跳轉csdn官網爲例,演示linkText定位:

    // 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表達式):
在這裏插入圖片描述

6 xpath定位

所謂xpath,即根據元素的路徑進行定位。更多xpath的知識請見:w3school

6.1 路徑匹配

xpath定位最常用的就是路徑定位了,具體又分爲絕對路徑和相對路徑。

路徑匹配有以下幾個符號:

  • /表示節點路徑,如/A/B/C表示節點A的子節點B的子節點C/表示根節點。
  • //表示所有路徑以//後指定的子路徑結尾的元素,如//D表示所有的D元素;如果是//C/D表示所有父節點爲CD元素。
  • *表示路徑的通配符,如/A/B/C/*表示A元素下的B元素下的C元素下的所有子元素。

6.1.1 絕對路徑

絕對路徑也稱全路徑,是指從根路徑出發,逐層定位,例如:

By.xpath("html/body/div/form/span/input")

以上面的百度搜索框爲例,絕對路徑:

By.xpath("/html/body/div[1]/div[1]/div[5]/div/div/form/span[1]/input")

絕對路徑繁瑣冗長,而且極易受前端結構變動影響,所以強烈不推薦使用。

6.1.2 相對路徑

即相對於上下文節點的路徑,使用雙斜槓,例如:

By.xpath("//input//div")

相對路徑更加實用,一般我們難以直接定位到一個目標元素時,可以先定位到一個能準確定位到的上級元素,再從此上級元素出發,通過元素之間的層級關係定位到目標元素。

例如,定位百度搜索框(當然百度搜索框本身是能定位到的,這裏是爲了演示):

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的元素。

例如:

By.xpath("//form[2]")

通過相對路徑定位元素,其核心思想在於,當目標元素不能直接定位時,先找到一個能直接定位到的元素,我稱之爲錨點元素,再通過目標元素與錨點元素之間的位置關係進行定位。

具體又可分爲,通過上下級節點(或父子節點)和同級節點(或兄弟節點)兩種方式。

示例代碼:

<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、通過父級節點查找子級節點

By.xpath("//div[@id='parent']/div[2]")

2、通過子級節點查找父級節點

By.xpath("//div[@id='B']/..")

3、通過兄弟節點定位

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元素

下面用一個示例進行演示:
在這裏插入圖片描述
頁面代碼如下,可以發現目標元素所屬的tbody標籤只有一個動態id,顯然難以直接定位。但在目標元素所在的tbody上面,有一個可以通過id直接定位到的tbody,我稱之爲錨點元素。錨點元素和目標元素所在tbody在同級,這時候就很適合用兄弟元素的方式去定位。
在這裏插入圖片描述
具體代碼如下:

By.xpath("//*[@id='separatorline']/following-sibling::tbody[2]/tr/th/a[3]")).click();

6.2 屬性匹配

在xpath中可以使用屬性和屬性的值來定位元素,使用屬性定位時要以@開頭(下面form僅爲示例,也可以爲divinput等)

  • //form[@id]:表示所有具有屬性idform元素。
  • //form[@*]:表示所有具有屬性的form元素。
  • //form[not(@*)]:表示所有不具有屬性的form元素。
  • //form[@id="myId"]:表示id值爲myIdform元素。

另外還有屬性模糊匹配方法,功能更加強大:

  • //form[start-with(@id,'myId')]:表示所有屬性id的值以myId開頭的form元素。
  • //form[ends-with(@id,'myId')]:表示所有屬性id的值以myId結尾的form元素。
  • //input[contains(@id,'myId')]:表示所有屬性id的值包含myIdform元素。
  • //a[contains(text(),'貼吧')]:表示超鏈接元素的文本內容爲貼吧的a元素

在實際定位中,常常是上面三種方式結合起來進行定位。

當然,另外還有使用布爾邏輯計算定位,例如:

By.xpath("//div[@id='myId' or @name='myName']")

雙條件同時過濾,例如:

By.xpath("//div[@id='myId'][@name='myName'")

7 cssSelector定位

更詳細內容請見:W3C

css元素選擇器

選擇input類型的元素:

By.cssSelector("input")

7.1 css類與id選擇器

id選擇器以 # 來定義,class類選擇器以一個.顯示,有以下幾種例子:

  1. 選擇idmyId的元素:
    By.cssSelector("#myId")
    
  2. 選擇idmyIdinput元素:
    By.cssSelector("input#myId")
    
  3. 選擇class爲a的元素:
    By.cssSelector(".a")
    
  4. 選擇class爲a、b的元素:
    By.cssSelector(".a.b")
    
  5. 選擇class爲a的input元素:
    By.cssSelector("input.a")
    

7.2 css屬性選擇器

  1. 選擇有屬性有屬性maxlengthinput元素
    By.cssSelector(“input[maxlength]”)
    
  2. 屬性maxlength的值精確等於255input元素(完全相等,區分大小寫)
    By.cssSelector(“input[maxlength=‘255’]”)
    
  3. 屬性class的值以空格隔開,其中一項等於fm(區分大小寫)input元素:
    By.cssSelector(“input[class~=‘fm’]”)
    
  4. 屬性class的值以bar開頭的div元素
    By.cssSelector(“div[class^=‘bar’]”)
    
  5. 屬性class的值以bar結尾的div元素
    By.cssSelector(“div[class$=‘bar’]”)
    
  6. 屬性name的值包含myNameform元素
    By.cssSelector(“form[name*=‘myName’]”)
    
  7. 屬性class有一個以“soutu”開頭的用連字符分隔的值列表(從左邊)的span元素:<span class="soutu-btn"></span>
    By.cssSelector(“span[class|=‘soutu’]”)
    
  8. 屬性type等於hidden且屬性name等於ch且屬性classbginput元素
    By.cssSelector("input[type='hidden'][name='ch'].bg")
    

7.3 css子元素選擇器

何爲子元素?以下面代碼中的form元素爲基準,span元素和a元素是它的子元素,但input元素不是。

代碼示例2:

<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元素:
    By.cssSelector("form#myForm >  span")
    
  2. 屬性idformform元素的子元素span元素的子元素input元素(實現百度搜索框定位):
    By.cssSelector("form#form > span > input")
    

7.4 css後代元素定位

後代元素與子元素的區別是,A元素的子元素的子元素,也是A的後代元素。同樣以前文代碼2中的form元素爲基準,span元素、a元素和input元素都是它的後代元素。

後代元素的標誌符號是空格,例如同樣定位百度搜索框,用後代元素方式:

By.cssSelector("form#form input[name='wd']")

7.5 css相鄰兄弟選擇器

相鄰兄弟選擇器(Adjacent sibling selector)可選擇緊接在另一元素後的元素,且二者有相同父元素。例如,屬性idmyId的form元素的後代div元素的span子元素的相鄰的第一個弟弟元素input

By.cssSelector("form#myId div>span+input")

7.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. 屬性classs_ipt_wrspan元素的第2個子元素,且其類型爲input的元素(位置和類型不對應則匹配失敗):
    By.cssSelector(“span[class~=‘s_ipt_wr’] > input:nth-child(2)”)
    
  2. 屬性classs_ipt_wrspan元素的倒數第3個子元素,且其類型爲input的元素(位置和類型不對應則匹配失敗)
    By.cssSelector(“span[class~=‘s_ipt_wr’] > input:nth-last-child(3)”)
    
  3. 屬性classs_ipt_wrspan元素的第2個input元素(該input元素在所有子元素排第幾無所謂):
    By.cssSelector(“span[class~=‘s_ipt_wr’] > input:nth-of-type(2)”)
    
  4. 屬性classs_ipt_wrspan元素的倒數第3個input元素(該input元素在所有子元素排第幾無所謂):
    By.cssSelector(“span[class~=‘s_ipt_wr’] > input:nth-of-type(3)”)
    
  5. 屬性classs_ipt_wrspan元素的子元素中排在第一且爲input類型的元素:
    By.cssSelector(“span[class~=‘s_ipt_wr’] > input:first-child”)
    
  6. 屬性classs_ipt_wrspan元素的子元素中排在最後一個且爲input類型的元素:
    By.cssSelector(“span[class~=‘s_ipt_wr’] > input:last-child”)
    
  7. 屬性classs_ipt_wrspan元素的第一個input類型的元素(該input元素在所有子元素排第幾無所謂):
    By.cssSelector(“span[class~=‘s_ipt_wr’] > input:first-of-type”)
    
  8. 屬性classs_ipt_wrspan元素的最後一個input類型的元素(該input元素在所有子元素排第幾無所謂):
    By.cssSelector(“span[class~=‘s_ipt_wr’] > input:last-of-type”)
    

基本上常用的css selector定位方式都在下表中了:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章