我试图用groovy为Swagger页面编写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)构造函数是一个可怕的眼睛痛!我试图用以下等待替换它:
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 = ...。
def resourceName = ...
这Thread.sleep(2000)是我现在可以进行此工作的唯一方法。我希望将其替换为对浏览器更友好/更可靠的等待,因此即使对于加载速度低于2秒的页面也可以使用。还有其他想法吗?
加载页面时,我在控制台中看到了"Loaded SwaggerUI"三遍。那就是你的问题:SwaggerUI被加载了3次。
"Loaded SwaggerUI"
所以我这样做:
我在打印出来的行上放置了一个断点"Loaded SwaggerUI"。
我重装了。
当我达到断点时,我拍摄了具有该类的元素的快照resource:
resource
var snap1 = Array.prototype.slice.call( document.getElementsByClassName("resource"))
(您必须将返回的值复制到一个Array(带有slice),因为它getElementsByClassName返回一个 实时 集合。)
Array
slice
getElementsByClassName
我点击了调试器的继续按钮。
当我再次达到断点时,我拍摄了第二张快照(名为snap2)。
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希望 完全相同的元素 而不是相同的副本。
StaleElementReferenceException