Azure Pipeline中测试偶发StaleElementReferenceException问题求助
我在Azure Pipeline中运行的测试有大约一半概率抛出StaleElementReferenceException,但本地(无头/非无头模式)运行全部通过。奇怪的是,报错的方法在其他多个测试中调用却没有问题。以下是调用栈和相关代码,请帮忙分析问题并给出调试建议。
调用栈
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute) at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters) at OpenQA.Selenium.WebDriver.InternalExecute(String driverCommandToExecute, Dictionary`2 parameters) at OpenQA.Selenium.WebElement.Execute(String commandToExecute, Dictionary`2 parameters) at OpenQA.Selenium.WebElement.get_Displayed() at SeleniumExtras.WaitHelpers.ExpectedConditions.<>c__DisplayClass6_0.<ElementToBeClickable>b__0(IWebDriver driver) at OpenQA.Selenium.Support.UI.DefaultWait`1.Until[TResult](Func`2 condition, CancellationToken token) at OpenQA.Selenium.Support.UI.DefaultWait`1.Until[TResult](Func`2 condition) at GembaCloud.Tests.Pages.BasePage.BasePageActions.WaitForElementToBeClickable(By element) in C:\agent\_work\53\s\GembaCloud.Tests\Pages\BasePage.cs:line 205 at GembaCloud.Tests.Pages.BasePage.BasePageActions.GetTable(By byTable, Int32 firstDataRowIndex) in C:\agent\_work\53\s\GembaCloud.Tests\Pages\BasePage.cs:line 259 at GembaCloud.Tests.Pages.GembaIntelligencePage.GembaIntelligencePageActions.GetGembaIntelligenceTable() in C:\agent\_work\53\s\GembaCloud.Tests\Pages\Home\GembaIntelligencePage.cs:line 52 at GembaCloud.Tests.Pages.GembaIntelligencePage.GembaIntelligencePageActions.ExpandGembaIntelligenceTableRow(Int32 rowNumber) in C:\agent\_work\53\s\GembaCloud.Tests\Pages\Home\GembaIntelligencePage.cs:line 79 at GembaCloud.Tests.TestClasses.UserRolePageElementAuthorisationTests.reporting_user_should_not_have_access_to_intelligence_dropdown_create_action_button() in C:\agent\_work\53\s\GembaCloud.Tests\TestClasses\UserRolePageElementAuthorisationTests.cs:line 115
相关代码
GembaIntelligencePage类
public void ExpandGembaIntelligenceTableRow(int rowNumber) { this.ExpandTableRow(this.GetGembaIntelligenceTable(), rowNumber); } public IList<IWebElement> GetGembaIntelligenceTable() { return this.GetTable(_elements.byGembaIntelligenceTable, 1); }
BasePage类
protected IList<IWebElement> GetTable(By byTable, int firstDataRowIndex) { //firstDataRowIndex is so we only return the table rows that we need this.WaitForElementToBeClickable(byTable); IWebElement table = _driver.FindElement(byTable); IList<IWebElement> listOfDataTableRows = table.FindElements(By.CssSelector("tr")).Skip(firstDataRowIndex -1).ToList(); return listOfDataTableRows; } protected void WaitForElementToBeClickable(By element) { WebDriverWait wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(10)); wait.Until(ExpectedConditions.ElementToBeClickable(element)); }
代码问题分析
等待条件未处理StaleElementReferenceException
调用栈显示错误发生在WaitForElementToBeClickable方法中,具体是ExpectedConditions.ElementToBeClickable内部检查元素Displayed属性时抛出异常。WebDriverWait默认仅忽略NotFoundException,但当页面异步更新导致元素被重新渲染时,之前找到的元素会变为stale,此时get_Displayed()会直接抛出StaleElementReferenceException,而等待逻辑不会重试查找。重复查找元素存在时间窗口风险
GetTable方法中,先调用WaitForElementToBeClickable等待元素可点击,之后再调用FindElement重新查找元素。两次查找之间如果页面发生更新(比如AJAX请求完成),新查找的元素可能已经处于stale状态,或者等待的元素和后续查找的元素不是同一个实例。返回缓存的元素列表易引发后续问题
返回的IList<IWebElement>是直接从table中获取的行元素缓存,若后续页面有更新,这些元素会立即变stale,不过当前报错发生在获取table阶段,所以优先解决前两个问题。
调试与修复建议
修改等待方法,添加异常忽略
更新WaitForElementToBeClickable,让等待逻辑在遇到StaleElementReferenceException时重试查找,同时直接返回等待到的元素避免重复查找:protected IWebElement WaitForElementToBeClickable(By element) { WebDriverWait wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(10)); wait.IgnoreExceptionTypes(typeof(StaleElementReferenceException), typeof(NotFoundException)); return wait.Until(ExpectedConditions.ElementToBeClickable(element)); } protected IList<IWebElement> GetTable(By byTable, int firstDataRowIndex) { IWebElement table = this.WaitForElementToBeClickable(byTable); IList<IWebElement> listOfDataTableRows = table.FindElements(By.CssSelector("tr")).Skip(firstDataRowIndex -1).ToList(); return listOfDataTableRows; }增加页面稳定性等待
在查找table前,添加针对页面异步加载的等待,比如等待页面加载完成:WebDriverWait wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(10)); wait.Until(driver => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").ToString() == "complete");排查环境差异
检查Azure Pipeline中的浏览器版本、Selenium驱动版本是否与本地一致;测试环境网络延迟可能更高,可适当延长等待时间(如从10秒调整为15秒)。添加调试日志与截图
在Azure Pipeline中配置测试日志,记录元素查找时间、页面状态;在异常发生时自动截图,帮助定位具体场景下的页面变化。
内容的提问来源于stack exchange,提问作者Keiggo

