Groovy Geb的一般问题(StaleElementReference异常和等待超时)

Groovy Geb的一般问题(StaleElementReference异常和等待超时),groovy,exception-handling,geb,staleobjectstate,Groovy,Exception Handling,Geb,Staleobjectstate,根据《Geb手册》,我开始绘制门户网站的网页。我更喜欢使用静态内容闭包块中定义的变量,然后在页面方法中访问它们: static content = { buttonSend { $("input", type: "submit", nicetitle: "Senden") } } def sendLetter() { waitFor { buttonSend.isDisplayed() } buttonSend.click() } 不幸的是,有时我会遇到Geb等待超时异

根据《Geb手册》,我开始绘制门户网站的网页。我更喜欢使用静态内容闭包块中定义的变量,然后在页面方法中访问它们:

static content = {
    buttonSend { $("input", type: "submit", nicetitle: "Senden") }
}
def sendLetter() {
    waitFor { buttonSend.isDisplayed() }
    buttonSend.click()
}
不幸的是,有时我会遇到Geb等待超时异常(60秒后),甚至更糟的是,我会收到众所周知的“StaleElementReferenceException”

在使用“isEnabled”而不是“isDisplayed”时,我可以避免等待超时,但对于“StaleElementReferenceException”,我只能应用以下解决方案:

def sendLetter() {
    waitFor { buttonSend.isEnabled() }
    try {
        buttonSend.click()
    } catch (StaleElementReferenceException e) {
        log.info(e.getMessage())
        buttonSend.click()
    }
}
我想,这个解决方案不是很好,但我不能像另一篇文章中描述的那样应用显式等待。因此,我有一些一般性问题:

  • 当页面是动态的时,我应该避免使用静态内容定义吗
  • Geb在什么时间或事件刷新其DOM?如何触发DOM茶点
  • 为什么在使用CSS选择器时仍然会出现“StaleElementReferenceException”

我将感谢每一个有助于理解或解决这个问题的提示。最好是有一个简单的代码示例,因为我还是初学者。谢谢大家!

如果在page类上定义了at检查,则页面将首先验证该条件并等待前n秒。在gebConfig文件中分配的。默认值为30秒

static at = {
    waitFor { buttonSend.isDisplayed() }
}
因此,一旦您使用测试调用页面的“to”方法,或者您正在为页面使用的任何方法,页面将等待,然后执行页面操作

to MyPage
buttonSend.click()
当页面是动态的时,我应该避免使用静态内容定义吗

不。实际上,静态定义是闭包。那是什么呢 实际上,每次使用静态页面时都会发生这种情况 正在调用闭包的组件,该闭包在 当前页面(webElements的集合)。理解这一点是成功的关键 使用Geb并发现您将遇到的问题

Geb在什么时间或事件刷新其DOM?如何触发DOM茶点

调用时:to、go、at、click、withFrame(frame,page)、withWindow 和浏览器驱动方法,它将刷新当前的 WebElements。Geb有一个很好的实用程序集来进行切换 页面之间和等待页面操作很容易。注:Geb为 实际上是建立在

为什么在使用CSS选择器时仍然会出现“StaleElementReferenceException”

可能页面尚未完成加载,已被操纵 使用ajax调用或以其他方式刷新。有时 “at”页面方法调用可以解决这些问题。它们最适合我 当使用框架作为Geb时,页面之间似乎变得混淆 还有一点框架。有一些变通办法

简言之,如果您使用页面模式,您可以使用使用静态内容、at和url闭包定义的页面类轻松切换预期页面,如下所示:

  • 至(第页)
  • 在(第页)
  • 导航器。单击(第页)
  • withFrame(框架,页面){}

除了twinj的答案之外,我还想指出一些其他解决方法,以防您遇到StaleElementReferenceException

  • 我经常发现,手动编写选择器比依赖页面中定义的内容更好。即使你的页面内容不应该被缓存,它们仍然会偶尔从我身边溜走。这在处理动态内容或迭代时尤其普遍

    比方说,我们想从动态创建的下拉列表中单击一个元素

    通常情况下,您可能希望执行以下操作

    static content = {
         dropdown { $("#parentDiv").find("ul") }
    }
    
    void clickDesiredElement(String elementName) {
         dropdown.click()
         def desiredElement = dropdown.find("li", text:elementName)
         waitFor { desiredElement.displayed }
         desiredElement.click()
    }
    
    def rows = $("#table1").find("tr").collect {
       [
          name: it.find("td",0),
          email: it.find("td",1)
       ]
    }
    
    如果这不起作用,请尝试完全删除内容,并手动写出选择器

    void clickDesiredElement(String elementName) {
         $("#parentDiv").find("ul").click()
         def desiredElement = $("#parentDiv").find("ul").find("li", text:elementName)
         waitFor { desiredElement.displayed }
         desiredElement.click()
    }
    
    在非常恶劣的情况下,您可能必须使用手动计时器,如中所述,您的代码可能如下所示

    void clickDesiredElement(String elementName) {
         $("#parentDiv").find("ul").click()
         sleepForNSeconds(2)
         def desiredElement = $("#parentDiv").find("ul").find("li", text:elementName)
         waitFor { desiredElement.displayed }
         desiredElement.click()
    }
    
    def rows = []
    int numRows = $("#table1").find("tr").size()
    int i
    for(i=0; i < numRows; i++) {
        waitFor {
          def row = $("#table1").find("tr",i)
          rows << [
              name: row.find("td",0),
              email: row.find("td",1)
          ]
        }
    }
    
    请记住,这是一个解决方法:)

  • 对于大型迭代和方便的闭包方法,例如each{}或collect{},您可能希望在每个迭代中添加waitFor{}

    假设我们想要得到一张大桌子的所有行

    通常情况下,您可能希望执行以下操作

    static content = {
         dropdown { $("#parentDiv").find("ul") }
    }
    
    void clickDesiredElement(String elementName) {
         dropdown.click()
         def desiredElement = dropdown.find("li", text:elementName)
         waitFor { desiredElement.displayed }
         desiredElement.click()
    }
    
    def rows = $("#table1").find("tr").collect {
       [
          name: it.find("td",0),
          email: it.find("td",1)
       ]
    }
    
    有时我发现自己不得不迭代地执行此操作,并在每次迭代之间使用waitFor{},以避免出现StaleElementReferenceException。它可能看起来像这样

    void clickDesiredElement(String elementName) {
         $("#parentDiv").find("ul").click()
         sleepForNSeconds(2)
         def desiredElement = $("#parentDiv").find("ul").find("li", text:elementName)
         waitFor { desiredElement.displayed }
         desiredElement.click()
    }
    
    def rows = []
    int numRows = $("#table1").find("tr").size()
    int i
    for(i=0; i < numRows; i++) {
        waitFor {
          def row = $("#table1").find("tr",i)
          rows << [
              name: row.find("td",0),
              email: row.find("td",1)
          ]
        }
    }
    
    def行=[]
    int numRows=$(“#表1”).find(“tr”).size()
    int i
    对于(i=0;irows我发现动态加载时,导航器会丢失。
    我已通过使用以下代码重新输入页面或模块,在本地解决了此问题:

    void waitForDynamically(Double timeout = 20, Closure closure) {
            closure.resolveStrategy = Closure.DELEGATE_FIRST
            switch (this) {
                case Module:
                    init(browser, browser.navigatorFactory)
                    break
                case Page:
                    init(browser)
                    break
                default:
                    throw new UnsupportedOperationException()
            }
    
            waitFor {
                closure()
            }
        }
    

    感谢这个详细的答案,让我们了解了Geb的魔力,这是任何使用Geb的人都必须做的。感谢这个非常有趣和详细的答案。有了你所有的例子,我再也不应该面对陈旧的元素了!:-)我想这个异常的主要原因是在访问页面元素之前缺少at checkers、 有时候,我只是引用了已经过时的页面对象。很酷,我又学到了更多关于Geb的知识!谢谢你,Jim!