2014-03-27 62 views
3

根据“Geb之书”,我开始映射我们门户的网页。我更喜欢使用静态内容闭合块中定义的变量,并在页面方法之后访问它们:不幸的是Geb的一般问题(StaleElementReferenceException和等待超时)

static content = { 
    buttonSend { $("input", type: "submit", nicetitle: "Senden") } 
} 
def sendLetter() { 
    waitFor { buttonSend.isDisplayed() } 
    buttonSend.click() 
} 

,有时我得到一个盖布等待超时异常(后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”?

我希望每个提示有助于理解或解决此问题。最好的办法是有一个简单的代码示例,因为我仍然是初学者。谢谢!

回答

3

除了twinj的回答,我想指出一些其他解决方法,以防遇到StaleElementReferenceException。

  1. 很多时候我发现最好是手动写出选择器,而不是依赖页面中定义的内容。尽管您的页面内容不应该被default缓存,但它们仍然有时会离开我。这在处理动态内容或迭代时尤为普遍。例如:

    例如:假设我们想要从动态创建的下拉列表中单击一个元素。

    通常你可能想要做这样的事情......

    static content = { 
        dropdown { $("#parentDiv").find("ul") } 
    } 
    
    void clickDesiredElement(String elementName) { 
        dropdown.click() 
        def desiredElement = dropdown.find("li", text:elementName) 
        waitFor { desiredElement.displayed } 
        desiredElement.click() 
    } 
    

    如果这不起作用,尝试摆脱完全的内容,并手动写出的选择...

    void clickDesiredElement(String elementName) { 
        $("#parentDiv").find("ul").click() 
        def desiredElement = $("#parentDiv").find("ul").find("li", text:elementName) 
        waitFor { desiredElement.displayed } 
        desiredElement.click() 
    } 
    

    在真是可恶情况下,您可能需要使用手动计时器,如this answer指出,和你的代码可能会是这样......

    void clickDesiredElement(String elementName) { 
        $("#parentDiv").find("ul").click() 
        sleepForNSeconds(2) 
        def desiredElement = $("#parentDiv").find("ul").find("li", text:elementName) 
        waitFor { desiredElement.displayed } 
        desiredElement.click() 
    } 
    

    请记住,这是一个解决办法:)

  2. 对于大型迭代和方便封闭方法,如每个{}或收集{},你可能想在每次迭代中添加一个WAITFOR {}。

    例:比方说,我们希望得到一个大表

    通常你可能想要做这样的事情的所有行...

    def rows = $("#table1").find("tr").collect { 
        [ 
         name: it.find("td",0), 
         email: it.find("td",1) 
        ] 
    } 
    

    有时候我发现自己不得不反复地做到这一点,以及每次迭代之间的waitFor {}以避免出现StaleElementReferentException。它可能看起来像这样...

    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) 
         ] 
        } 
    } 
    
+0

这个非常有趣和详细的回答非常感谢。用你的所有例子,我永远不会面对陈旧的元素! :-)我认为这个例外的主要原因是在访问页面元素之前缺少检查器。有时候,我只是使用了已经陈旧的页面对象的引用。酷,我又学到了更多关于Geb的知识!谢谢,吉姆! – AndyDoe

4

如果您在检查页面类时定义了一个页面,该页面将首先验证该状况并等待前n秒。这是在你的gebConfig文件中分配的。默认值是30秒。

static at = { 
    waitFor { buttonSend.isDisplayed() } 
} 

因此,一旦你打电话给你的网页“向”方法与测试或任何你正在使用它的页面将等待,然后执行你的页面的操作。

to MyPage 
buttonSend.click() 

我应该避免在页面动态时使用静态内容定义吗?

不,实际上,静态定义是闭包。那么 究竟发生了什么,每当你使用那个页面静态 组件时,你正在调用一个在当前页面(webElements集合)上动态运行的闭包。了解这一点是使用Geb的 的关键,并发现您遇到的问题。

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

当你打电话:可以去,在中,单击,withFrame(帧,页面),withWindow 和浏览器的驱动方法,它会刷新当前的一组 WebElements。 Geb拥有丰富的工具,可以在页面之间切换 并轻松地等待页面操作。注意:Geb是 实际上建立在WebDriver WebElements

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

这是可能的页面没有加载完成后,已被操纵 用Ajax调用或以其他方式已经被刷新。 'PAGE方法调用中的 有时可以解决这些问题。当使用框架时,他们对我来说最为普遍 ,因为Geb似乎在页面 和框架之间有点混淆。有解决方法。

总之,如果你使用的页面模式,你可以轻松地切换使用,你有一个静态的内容定义的Page类预期的网页,在和使用下面的链接关闭:

  • (第56页)
  • 在(页)
  • Navigator.click(页)
  • withFrame(帧,页){}
+0

感谢这个详细的回答给人的盖布的神奇内, – AndyDoe