2014-03-27 59 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