When I use WebDriverWait rather than Thread.sleep I get a StaleElementReferenceException

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-02 13:14:56

When I load your page, I see in the console "Loaded SwaggerUI" three times over. That's your problem: SwaggerUI is loaded 3 times over.

How to Figure This Out

So I did this:

  1. I put a breakpoint over the line that prints out "Loaded SwaggerUI".

  2. I reloaded.

  3. When I hit the breakpoint, I took a snapshot of the elements that have the class resource:

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

    (You have to copy the returned value to an Array (with slice here) because getElementsByClassName returns a live collection.)

  4. I hit the debugger's continue button.

  5. When I hit the breakpoint again, I took a second snapshot (named snap2).

Ok now for some tests. If the DOM has not changed, the elements should be identical:

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

That does not look good. Let's see what's still in the DOM tree:

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

The element in the first snapshot is no longer in the tree but the one in the 2nd is.

Why The Selenium Error?

The 2 second wait is enough to let Selenium start finding elements after the DOM has stabilized. However, when you tell Selenium to wait until there are visible elements of class resource in the page, it waits until SwaggerUI is loaded for the first time. At some point when it is processing the elements it first finds, SwaggerUI loads another time and then the old elements that it found are no longer in the DOM tree. So it raises a StaleElementReferenceException because the element it once found in the DOM tree is no longer there. It's been replaced by an element which is in the same location and structurally identical but Selenium wants the exact same element not an identical copy.

After the most excellent research / suggestion by @Louis, I ended up using:

def wait = new FluentWait<By>(By.className("resource")).
        withTimeout(10, TimeUnit.SECONDS).
        ignoring(NoSuchElementException)
wait.until(new Function<By, Boolean>() {
            WebElement res
            Boolean apply(By by) {
                def oldRes = res
                res = driver.findElement(by)
                return res == oldRes
            }
        })

If anyone is interested, the entire Builder can be found on SourceForge (still under construction at the time of this writing).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!