您當前的位置:首頁 > 收藏

八年測試經驗,吐血整理出Selenium無法定位元素的幾種解決方案

作者:由 軟體測試小P 發表于 收藏時間:2022-01-20

WebDriver只能在一個頁面上對元素識別與定位,對於frame/iframe表單內嵌的頁面元素無法直接定位。

解決方法

driver。switch_to。frame

(id/name/obj)

switch_to。frame()預設可以直接取表單的id或name屬性。如果沒有可用的id和name屬性,可以先定位到frame/iframe,再將定位物件傳給switch_to。frame(物件)方法。

xf = driver。find_element_by_xpath(‘//*[@class=“if”]’)

driver。switch_to。frame(xf)

。。。

driver。switch_to。parent_frame() 切到父frame。影響效能,可以提給開發,讓其改進。

driver。switch_to。default_content() 跳回最外層的頁面

02

頁面跳轉到新標籤頁,或彈出警告框等

在頁面操作過程中有時候點選某個連結會彈出新視窗,這時就需要切換焦點到新視窗上進行操作。

解決方法1

:driver。switch_to。window(

window_handle

)切換到新視窗。首先獲取當前視窗的控制代碼driver。current_window_handle,接著開啟彈出新視窗,獲得當前開啟的所有視窗的控制代碼driver。window_handles。透過for迴圈遍歷handle,如果不等於第一次開啟視窗的控制代碼,那麼一定是新視窗的控制代碼,因為執行過程只打開了兩個視窗;改變條件,如果等於第一次開啟視窗的控制代碼,那麼可以切換回第一次開啟的視窗。

解決方法2

:對於JavaScript生成的alert、confirm以及prompt,無法使用前端工具對彈出視窗進行定位的,使用driver。switch_to。alert方法定位彈出框。

alert的方法有:

。accept() ‘等同於點選“確認”或“OK”’

。dismiss() ‘等同於點選“取消”或“Cancel”’

。text ‘獲取alert文字內容,對有資訊顯示的alert框’

。send_keys(text) ‘傳送文字,對有提交需求的prompt框’

。authenticate(username,password) ‘驗證,針對需要身份驗證的alert’

03

頁面元素失去焦點導致指令碼執行不穩定

解決方法

driver。switch_to。active_element 遇到指令碼不穩定,有時會失去焦點導致測試失敗的情況下,可以先切到焦點元素再進行操作。注意。active_element後面不帶括號()。

下面是一個參考案例:

‘最初的 “右擊滑鼠 → 新建資料夾 → 輸入資料夾名稱” 的程式碼’

l = driver。find_element_by_id(‘pm_treeRoom_1_span’)

ActionChains(driver)。context_click(l)。perform()

driver。find_element_by_class_name(‘fnew’)。click()

time。sleep(2)

driver。find_element_by_xpath(‘//*[@id=“pm_treeRoom_1_ul”]/li[。。。]’)。send_keys(‘filename’)

time。sleep(2)

結果這種操作總會導致輸入框失去焦點,直接消失,更不能send_keys進去了,直接報錯。

‘修改後的程式碼如下’

driver。find_element_by_class_name(‘fnew’)。click()

time。sleep(2)

driver。switch_to。active_element。send_keys(‘filename’)

time。sleep(2)

04

使用Xpath或CSS定位

find_element_by_xpath(“//標籤[屬性=‘值’]”)

使用Xpath/CSS方法,非常適合定位屬性值動態生成、不容易定位的元素。如果不想指定標籤,則可以使用“*”代替,使用xpath不侷限於id、name和class這三個屬性,元素的任意屬性值都可以使用,只要它能唯一的標識一個元素。

解決方法1

:如果一個元素沒有唯一屬性,那麼我們可以一級一級向上查詢,直到找到可以唯一定位元素的屬性,再向下查詢其子元素。

find_element_by_xpath(“//form[@id=‘form’]/span[2]/input”) 首先透過唯一標識屬性id=form定位最外層元素,接著找到最外層元素下的第2個span標籤的元素為父元素,最後向下查詢定位到父元素下標籤為input的子元素。

解決方法2

:如果一個屬性不能唯一地區分一個元素,那麼使用多個屬性來唯一地定位一個元素。

find_element_by_xpath(“//input[@id=‘kw’and@class=‘su’]/span/input”) 首先找到標籤為input,id=kw且class=su的元素,接著找到其下標籤為span的子元素,繼續向下查詢找到標籤為input的子元素。

解決方法3

:檢查Xpath描述是否有誤,導致無法定位到元素。

05

頁面還沒加載出來就對頁面上的元素進行操作

因為載入元素延時造成的指令碼失敗,我們可以透過設定等待時間來提升自動化指令碼的穩定性。

解決方法1

WebDriverWait()顯示等待。等待單個的元素載入,通常配合until()、until_not()方法使用。

八年測試經驗,吐血整理出Selenium無法定位元素的幾種解決方案

即,

WebDriverWait(driver, 超時時長, 呼叫頻率, 忽略異常).until(可執行方法, 超時時返回的資訊)

WebDriverWait(driver,5,1)。until(expected_conditions。presence_of_element_located(By。ID,‘kw’))

最長等待時間為5s,每隔1秒檢查一次id=‘kw’的元素是否被載入在DOM樹裡(並不代表該元素一定可見)。最常用的

method

是expected_conditions類提供的

預期條件判斷。

is_disappeared= WebDriverWait(driver, 30, 1, (ElementNotVisibleException))。until_not(lambda x: x。find_element_by_id(‘someId’)。is_displayed())

最長等待時間為30s,每隔1秒檢查一次id=‘someId’的元素是否從DOM樹裡消失,忽略預設異常資訊

NoSuchElementException

和指定的異常資訊

ElementNotVisibleException

。此處匿名函式lambda的用法具體參考Python語法。

解決方法2

driver。implicitly_wait(秒) 隱式等待。全域性等待,對所有元素設定超時時間,等待頁面的載入,因此只需要設定一次即可。這裡的時間是最長等待時間(非固定等待時間)。

解決方法3

sleep(秒)執行緒等待。休眠固定的時間,使用時需要先引入time模組的sleep方法from time import sleep。

06

元素被遮擋,不可用,不可見

解決方法1

:driver。maximize_window()由於視窗大小改變引起的頁面元素佈局發生變化,被測元素被遮擋,可以先將視窗最大化,再進行元素定位。

解決方法2

:。is_enabled()由於業務原因元素在某些情況下不可用(元素屬性disabled,灰顯),首先檢查測試步驟是否符合業務邏輯,其次確認是否為業務流程上的Bug。

解決方法3

:。is_displayed()對於屬性不一定可見的元素,在定位前首先判斷其屬性是否可見,是否被隱藏。

解決方法4

:由於佈局不合理導致的元素被遮蓋、或是元素本身缺失引起的無法定位問題屬於Bug,可以提給開發讓其改進。

07

用WebDriver呼叫JavaScript程式碼代替無法實現的功能

對於有些WebDriver沒有提供的方法或者無法實現的功能,WebDriver提供了driver。execute_script()方法來執行JavaScript程式碼。

解決方法

如果頁面內容過長,視窗最大化也無法檢視到所有元素,可以透過執行JavaScript指令碼實現捲軸的拖動等動作。

driver。execute_script(“window。scrollTo(0, document。body。scrollHeight);”)

以上語句實現了拉動頁面到底部的功能,其中window。scrollTo(左邊距,上邊距)是JavaScript中用於設定瀏覽器視窗捲軸的水平和垂直位置的程式碼。

text = “input text”

driver。execute_script(“var obj=document。getElementById(‘text’); obj。value=‘ ” + text + “ ’;”)

假設一個輸入框可以透過id=‘text’將其定位,卻不能透過send_keys()輸入文字內容,可以藉助JavaScript程式碼來實現。

video = driver。find_element_by_xpath(“body/Section[1]/div/video”)

url = driver。execute_script(“return arguments[0]。currentSrc;”, video)

print(url) ‘返回檔案播放地址’

print(“start”) ‘播放視屏’

driver。execute_script(“return arguments[0]。play()”, video)

sleep(15) ‘播放15秒鐘’

print(stop) ‘暫停視屏’

driver。execute_script(“arguments[0]。pause()”, video)

。。。

以上實現了HTML5視屏

其中arguments是JavaScript的內建物件。因為將video物件傳給了arguments,所以arguments[0]相當於JavaScript指令碼的document。getElementsByTagName(“video”)。JavaScript不支援過載,使用arguments物件可以模擬函式過載效果。

08

WebDriver無法操作Windows控制元件

檔案的普通上傳和下載(參考How to auto save files using custom Firefox profile ?),可以透過。。send_keys(‘本地路徑’)和find_element_by_partial_link_text(‘下載連結名’)。click()實現。

解決方法

對於外掛上傳,需要操作Windows控制元件的,可以透過安裝AutoIt工具、編寫指令碼、儲存為“。au3”檔案、轉換成“。exe”檔案,再由自動化指令碼os。system(“D:\\upfile。exe”)實現上傳/下載。

* 雖然這種方法可以解決檔案上傳、下載的操作問題,

但是並不推薦。

因為透過python呼叫exe程式並不在python的可控範圍內,執行多長時間,執行過程是否出錯,都無從自動化過程得知。

09

firefox安全性強,不允許跨域調用出現報錯

錯誤描述:

uncaught exception: [Exception。。。 “Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIDOMNSHTMLDocument。execCommand]” nsresult: “0x80004005 (NS_ERROR_FAILURE)” location:

解決辦法

Firefox 要取消XMLHttpRequest的跨域限制的話,

第一

是從 about:config 裡設定 signed。applets。codebase_principal_support = true;(位址列輸入about:config 即可進行firefox設定);

第二

就是在open的程式碼函式前加入類似如下的程式碼:

try {

netscape。security。PrivilegeManager。enablePrivilege(“UniversalBrowserRead”);

} catch (e) {

alert(“Permission UniversalBrowserRead denied。”);

}

標簽: driver  元素  element  id  Find