一尘不染

当我使用WebDriverWait而不是Thread.sleep时,我得到了StaleElementReferenceException

selenium

我试图用groovySwagger页面编写Selenium对象构建器。为了便于讨论,我的问题代码可以简化为以下几种:

class SwaggerBuilder {

    WebDriver driver
    def resources

    SwaggerBuilder(WebDriver driver) {

        this.driver = driver

        Thread.sleep(2000)

        resources = driver.findElements(By.className("resource")).collectEntries {
            def resourceName = it.findElement(By.tagName("a")).getText().replaceFirst("[/]", "")
            [(resourceName): it]
        }
    }

    Object invokeMethod(String name, Object args) {

        if(resources[(name)] == null)
            throw new NoSuchElementException("Resource $name cannot be found.")

        resources[(name)].findElement(By.linkText("List Operations")).click()
    }
}

调用它非常简单(来自JUnit3):

void test1() {

    driver = new FirefoxDriver()
    driver.get("http://petstore.swagger.wordnik.com/")

    def petstore = new SwaggerBuilder(driver)    // problem does not get past this line!
    try {
        petstore.not_there()
        fail("Did not catch exception.")
    } catch(NoSuchElementException ex) {
        assertTrue(ex.message.startsWith("Resource not_there cannot be found."))
    } catch(Exception ex) {
        fail("Caught wrong exception: ${ex.class}.")
    }
}

Thread.sleep(2000)构造函数是一个可怕的眼睛痛!我试图用以下等待替换它:

def wait = new WebDriverWait(driver, 20)
wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("resource")))

要么:

    def wait = new FluentWait<By>(By.className("resource")).
            withTimeout(20, TimeUnit.SECONDS).
            pollingEvery(100, TimeUnit.MILLISECONDS).
            ignoring(StaleElementReferenceException)
    wait.until(new Function<By, Boolean>() {
                def count = 0
                @Override
                Boolean apply(By by) {
                    def oldCount = count
                    count = driver.findElements(by).size()
                    return count == oldCount
                }
            })

两者都产生了相同的结果:以开头的行中的闭包中的“
org.openqa.selenium.StaleElementReferenceException:元素不再连接到DOM” def resourceName = ...

Thread.sleep(2000)是我现在可以进行此工作的唯一方法。我希望将其替换为对浏览器更友好/更可靠的等待,因此即使对于加载速度低于2秒的页面也可以使用。还有其他想法吗?


阅读 365

收藏
2020-06-26

共1个答案

一尘不染

加载页面时,我在控制台中看到了"Loaded SwaggerUI"三遍。那就是你的问题:SwaggerUI被加载了3次。

如何解决这个问题

所以我这样做:

  1. 我在打印出来的行上放置了一个断点"Loaded SwaggerUI"

  2. 我重装了。

  3. 当我达到断点时,我拍摄了具有该类的元素的快照resource

    var snap1 = Array.prototype.slice.call(
                document.getElementsByClassName("resource"))
    

(您必须将返回的值复制到一个Array(带有slice),因为它getElementsByClassName返回一个 实时 集合。)

  1. 我点击了调试器的继续按钮。

  2. 当我再次达到断点时,我拍摄了第二张快照(名为snap2)。

现在可以进行一些测试了。如果DOM没有更改,则元素应相同:

> snap1[0] === snap2[0]
false

看起来不太好。让我们看看DOM树中仍然有什么:

> document.contains(snap1[0])
false
> document.contains(snap2[0])
true

第一个快照中的元素不再在树中,而第二个快照中的元素在树中。

为什么硒错误?

2秒的等待时间足以让Selenium在DOM稳定之后开始查找元素。但是,当您告诉Selenium等待直到页面中有可见的class元素时resource,它就会等待直到
第一次 加载SwaggerUI 。在处理第一次找到的元素时,SwaggerUI 会加载另一个时间
,然后发现的旧元素不再在DOM树中。因此它提出了一个问题,StaleElementReferenceException因为它在DOM树中曾经发现的元素不再存在。它已替换为位于相同位置且结构相同的元素,但Selenium希望
完全相同的元素 而不是相同的副本。

2020-06-26