Selenium WebDriver在表元素的getText()上生成StaleElementReferenceExeption

时间:2013-12-11 17:00:22

标签: selenium selenium-webdriver

当前环境:

  • Selenium Server版本2.37.0
  • 在Firefox上运行的RemoteWebDriver
  • 没有Ajax /异步加载的内容

我的测试正在尝试验证HTML表格的每个单元格的内容。在访问任何表元素之前,显式等待验证< tbody>元素存在

ExpectedCondition<WebElement> recruitTableIsPresent = ExpectedConditions.presenceOfElementLocated(By.id("newRecruitFieldAgentWidget:newRecruitDataTable_data"));
new WebDriverWait(driver, 5).until(recruitTableIsPresent);

验证表存在后,按行和列

提取数据
private Stats[] parseStats() {
    String xpath = "//tbody[@id='regionalFieldAgentWidget:regionalDataTable_data']/tr[%d]/td[%d]";
    Stats[] stats = new Stats[3];
    for (int i = 0; i < stats.length; i++) {
        String inProgresOrders = cellContent(xpath, i, 1);
        String maxCapacity = cellContent(xpath, i, 2);
        String allocationRatio = cellContent(xpath, i, 3);
        Stats[i] = new Stats(inProgressORders, maxCapacity, allocationRatio);
    }
    return stats;
}

private String cellContent(String xpathTemplate, int row, int cell) {
    String xpath = String.format(xpathTemplate, row + 1, cell + 1);
    new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.xpath(xpath)));
    WebElement elementByXPath = driver.findElementByXPath(xpath);
    return elementByXPath.getText();
}

我没有看到任何竞争条件,因为表格内容是用页面填充的,而不是在异步调用中。另外,我已经看到其他建议通过驱动程序实例调用findElement()将刷新缓存。最后,在访问元素之前的显式等待应该确保&lt; TD&gt;标签存在。

可能导致getText()方法返回以下异常:

  

org.openqa.selenium.StaleElementReferenceException:在缓存中找不到元素 - 自查询以来页面可能已更改

值得注意的是,失败是间歇性的。某些执行失败而其他执行通过相同的代码传递。导致失败的表格单元也不一致。

2 个答案:

答案 0 :(得分:0)

使用Html-Agility-Pack可以解决这个问题。
只有当您想要从该页面读取数据时,这才有效。

这就像这样

//Convert the pageContent into documentNode.

void _getHtmlNode(IWebDriver driver){
    var htmlDocument = new HtmlDocument();
    htmlDocument.LoadHtml(driver.PageSource);
    return htmlDocument.DocumentNode;
}

private Stats[] parseStats(){
    String xpath = "//tbody[@id='regionalFieldAgentWidget:regionalDataTable_data']/tr[%d]/td[%d]";
    Stats[] stats = new Stats[3];
    for (int i = 0; i &lt; stats.Length; i++) {
        String inProgresOrders = cellContent(xpath, i, 1);
        String maxCapacity = cellContent(xpath, i, 2);
        String allocationRatio = cellContent(xpath, i, 3);
        Stats[i] = new Stats(inProgressORders, maxCapacity, allocationRatio);
    }
    return stats;
}

private String cellContent(String xpathTemplate, int row, int cell) {
    String xpath = String.format(xpathTemplate, row + 1, cell + 1);
    new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.xpath(xpath)));
    var documentNode = _getHtmlNode(driver);
    var elementByXPath = documentNode.SelectSingleNode(xpath);
    return elementByXPath.InnerText;
}

现在读取任何数据。

有关使用htmlNode的一些提示。
1。与driver.FindElement: document.SelectSingleNode类似
2。与driver.FindElements类似: document.SelectNodes
3。与driver.Text类似: document.InnerText。



有关HtmlNode的更多搜索。

答案 1 :(得分:0)

事实证明,我已经提到了竞争条件。由于jQuery可以通过PrimeFaces获得,因此在其他一些帖子中提到了一个非常方便的解决方案。我实现了以下方法来等待任何异步请求在解析页面元素之前返回

public static void waitForPageLoad(JavascriptExecutor jsContext) {
    while (getActiveConnections(jsContext) > 0) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }
    }
}

private static long getActiveConnections(JavascriptExecutor jsContext) {
    return (Long) jsContext.executeScript("return (window.jQuery || { active : 0 }).active");
}

每个内置的驱动程序实现都实现了JavascriptExecutor接口,因此调用代码非常简单:

WebDriver driver = new FirefoxDriver();
waitForPageLoad((JavascriptExecutor) driver);