【Selenium】stale element reference 問題解決方案

問題現象

如截圖所示,異常原因:stale element reference: element is not attached to the page document
在這裏插入圖片描述

用例執行環境

java 1.8
selenium 3.141.0
docker(chromedriver 2.43 + chromebrowser 70.0.3538.77)

出現該問題的場景

autoOperate.verify_element_exists(driver, driverOperBean);  //判斷元素是否存在
List<WebElement> webElements = driver.findElements(By.cssSelector(elements));
if(webElements == null || webElements.isEmpty()){
    throw new CheckDriverException(String.format("要編歷的元素列表爲空; case信息: %s;", driverOperBean.toString()));
}
if(start> webElements.size()){
    throw new CheckDriverException(String.format("編歷的元素列表長度不足; 元素列表長度:[%s], 要編歷的起始位置:[%s]; 用例行數: %s;",webElements.size(),start,driverOperBean.getNum()));
}
int forLen = webElements.size();
if(end>0 && end<=forLen){
    forLen=end;
}
for(int i=start;i<=forLen;i++){
    String param = driverOperBean.getCustomizename();
    List<DriverOperBean> driverOperBeans = driverOperBean.getDriverOpers();
    if(StringUtils.isNotEmpty(param)){ 
        autoOperate.setParamElMap(driver.hashCode(), param, webElements .get(i-1));
    }
    goAutoCase(driver, autoCaseinfoProcess.copyDriverOperBeanlist(driverOperBeans)); 
}

邏輯是:根據一個定位獲取一個元素list,然後遍歷,遍歷會涉及點擊類操作;

分析問題

這裏看到整個獲取元素和遍歷邏輯是沒有問題,但是在執行遍歷的過程中有的case能執行通過,有的會在遍歷點擊的時候報stale element reference: element is not attached to the page document錯誤(任何操作元素的方法都會報這個錯誤);這個錯誤是什麼意思呢?從翻譯的字面意思是,這個元素沒有掛載到dom樹上(這裏就不說dom樹的概念了)。

沒有在dom樹上?
通過case直接的對比,發現一個問題,能夠通過的case是因爲這個case中的元素是寫死在html文件裏的,不通過的是因爲遍歷的元素是通過js渲染的。

寫死在html和js渲染的有什麼區別呢?
url進行網絡請求的時候拿到的最原始的內容其實是html源碼,可以直接看到內容;還有一種就是直接通過js渲染,呈現在瀏覽器網頁上;這個應該容易理解,對於我們遍歷的元素,代碼舉例如下(示例意在即可):

  • 可點擊的
<div>
	<div><a></div>
	<div><a></div>
	<div><a></div>
</div>

for循環點擊a標籤的時候,是能夠拿到當前瀏覽器已經渲染好的完整dom樹結構下的標籤元素(這個獲取的元素是唯一的),在循環點擊的a標籤的時候,其他a標籤是不會變的,所以可以順利執行,因爲在for之前的getElementsBy那個list內容,在循環的click或其他api操作下不會改變的。

  • 不可點擊的
<div id='aa'></div>   //頁面源碼

$("#aa").append([<div><a></div>, <div><a></div>, <div><a></div>]) //用js渲染

$('a').on('click', functino(){				//爲每個a標籤加個點擊事件
	$("#aa").append([<div><a></div>, <div><a></div>, <div><a></div>])
})

這裏想說明點就是,出現報錯的原因,當循環開始的時候,第一個元素點擊是正常的,因爲list裏的對象在點擊前是沒有變化的,點擊後呢?dom樹內容已經更新了,雖然是以同名同姓的方式填入,但還是變化了,所以還是按原來getElementsBy獲取到的那個list遍歷,肯定不對的 ,現實頁面中對應的同名元素對象已經更新了,這個官網有詳細的解釋,所以面對改變,我們要做的是拿着那個定位方式,再來一遍。

解決後的代碼

List<WebElement> webElements = driver.findElements(By.cssSelector(elements));
...省略同上
for(int i=start;i<=forLen;i++){
    String param = driverOperBean.getCustomizename();
    List<DriverOperBean> driverOperBeans = driverOperBean.getDriverOpers();
    if(StringUtils.isNotEmpty(param)){  
        // **** 再次獲取需要循環的elements,防止dom刷新,即使沒有刷新也等同webElements
        List<WebElement> wes = driver.findElements(By.cssSelector(elements));
        if(wes.size() > webElements.size() && i > webElements.size()){
            i = webElements.size();     // **** 如果操作後的目標list發生變化,還按原來的list大小處理
        }
        autoOperate.setParamElMap(driver.hashCode(), param, wes.get(i-1));   // **** 取元素按最新wes的內容取
    }
    goAutoCase(driver, autoCaseinfoProcess.copyDriverOperBeanlist(driverOperBeans));  //執行selenium api方法
}

備註 :這個問題的引發原因應該會出現在很多場景裏(不限於文中的for循環),但是處理方案就是保證執行操作前,這個元素是最新的並且可進行操作

參考文獻

Stale Element Reference Exception

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