selenium webdriver 深入理解各元素定位方法
- 概要
選單欄,由於安裝不同的元件導致錄製的xpath發生變化。
左側樹,由於左側樹由於業務會經常發生變動,導致錄製xpath發生變化。
列表資訊,通常一個列表裡面包含N多資料,我們不可以挨個去錄製。且列表一般都是可配置列的。
動態id,很多彈出頁面,彈出框之類的頁面大多都使用的是動態id
為解決此類問題,我們要學會活用selenium為我們提供的多種定位方式。 用以達到用例穩定執行的目標。
- By.id(id)、By.xpath(xpath);使用最多的定位方式
- By.linkText(linkText),By.partialLinkText(linkText) a標籤的不二之選
- By.tagName(name) 特別好用的定位方式
- By.className(className)可以解決很大的問題
- By.cssSelector(selector) 超級無敵好用
- 使用最多的定位方式By.id(id)、By.xpath(xpath)
By.id定位是最高效也是首選的方法用於查詢一個元素。UI 開發人員常犯的錯誤是,要麼沒有指定id,要麼自動生成隨機 id,這兩種情況都應避免。及時是使用 class 也比使用自動生成隨機 id要好的多。
HTML:
<div id="coolestWidgetEvah">...</div>
Java:
WebElement element = driver.findElement(By.id("coolestWidgetEvah"));
By xpath: 在沒有id的場景中。 最常用的是xpath。無所不能的定位方式。 類似如下所示。
定點陣圖片:
xpath=//img[@alt='image alt text goes here']
定位表格:
xpath=//table[@id='table1']//tr[4]/td[2]
xpath=(//table[@class='nice'])//th[text()='headertext']/
定位a標籤
xpath=//a[contains(@href,'href goes here')]
xpath=//a[contains(@href,'#id1')]/@class
定位input
xpath=//input[@name='name2' and @value='yes']
Java:
WebElement element = driver.findElement(By.xpath(xpath));
- 被遺忘的定位方式:
- By.linkText,By.partialLinkText a標籤的不二之選
By Link Text :查詢連結文字匹配的連結元素。
HTML:
<a href="http://www.google.com/search?q=cheese">cheese</a>>
Java:
WebElement cheese = driver.findElement(By.linkText("cheese"));
By Partial Link Text:查詢連結文字部分匹配的連結元素。
HTML:
<a href="http://www.google.com/search?q=cheese">search for cheese</a>>
Java:
WebElement cheese = driver.findElement(By.partialLinkText("cheese"));
在我們http://www.seleniumeasy.com/test/頁面為例:
例如: 點選報表列表。
點選選單:
我們通過直接錄製點選報表的形式因為安裝的控制元件原因會導致xpath等發生變化導致定位失敗。
此時我們若使用類似如下方法, 將使用By.partialLinkText(reportmenu)的方式直接點選選單名稱。 會很大程度提升用例的穩定性。
public void clickMenu(String reportmenu) {
drivre.findElement(By.partialLinkText(reportmenu)).click();
}
- By.tagName特別好用的定位方式
By Tag Name 是一個特別好用的定位方式。 多用於都配合driver.findElements(By.tagName(name))使用。
例如:
下拉框:
列表:
樹形選單:
如上圖所示:
當我們需要處理類似多種資料時。 比如我們需要遍歷下拉列表。 遍歷獲取列表框中的資料。 點選樹狀選單時。 此時通過 By Tag Name 是最好的選擇方式。
如下程式碼,我們首先定位到下拉列表框。 然後通過By.tagName("div")),獲取自稱元素。
再通過element.findElements(By.tagName("div")) 的方式獲取我們所需要的所有的列表數值。
後續我們通過傳入的引數進行操作。 可以達到整個版本通用。
public static void selectByVisibleText(WebDriver driver, String visibleText) {
WebElement element = driver.findFirstElement(By.tagName("div"));
List<WebElement> elementList = element.findElements(By.tagName("div"));
logger.info("elementList.size():" + elementList.size());
for (WebElement webElement : elementList) {
String text = webElement.getText();
logger.info("text:" + text);
if (visibleText != null && text.contains(visibleText)) {
BasicAction.sleep(1);
webElement.click();
break;
}
}
}
當我們處理list列表的時候,只需要獲取到table的元素。 然後通過By.tagName("tr")) 獲取行元素
再通過By.tagName("td"))獲取列元素。
通過行列的兩層遍歷便可得到整個列表的元素資訊。
public static JSONArray getListInfo(WebElemnt table) {
JSONObject object = new JSONObject();
JSONArray array = new JSONArray();
List<WebElement> rows = table.findElements(By.tagName("tr"));
// logger.info("rows size: " + rows.size());
for (WebElement row : rows) {
if (row.getAttribute("class").contains("eview-table-tr")) {
List<WebElement> cols = row.findElements(By.tagName("td"));
if (cols.size() == 1) {
break;
} else {
for (WebElement col : cols) {
object.put(col.getAttribute("data-colid"), col.getText());
}
array.add(object);
}
}
}
return array;
}
- By.className可以解決很大的問題
如下所示,我們需要獲取彈出框中的提示資訊時。 當前頁面彈出框 沒有其他可定位的條件。 此時通過唯一的className定位是極好的。
public static String getMsgDialogText() {
WebDriver driver ;
String text = driver.findElement(By.className("eview-msg-content")).getText();
logger.info("MessageDialog Text:" + text);
return text;
}
- By.cssSelector超級無敵好用
定位類似標籤頁中有多個class的。 此時我們使用className定位不都準確。可以使用css的定位方式處理。
public static JSONObject getPortalNameJson(WebElement chartElement) {
JSONObject object = new JSONObject();
WebElement yElement = chartElement.findElement(By.cssSelector(".dual-list list-left.col-md-5"));
List<WebElement> divElements = yElement.findElements(By.className("list-group-item"));
Map<String, String> deviceMap = new LinkedHashMap<String, String>();
Map<String, String> devicePointMap = new LinkedHashMap<String, String>();
for (WebElement webElement : divElements) {
String title = null;
title = webElement.getText();
if (title.endsWith("...")) {
title = webElement.getAttribute("title");
title = "..." + title; //表示該裝置名稱未全部顯示
}
String index = webElement.getAttribute("index");
String namePoint = webElement.getLocation().toString();
devicePointMap.put(title, namePoint);
deviceMap.put(index, title);
}
object.put(NAME_MAP, deviceMap);
object.put(NAME_POINT_MAP, devicePointMap);
logger.info("getPortalNameJson object: " + object);
return object;
}