Passing a Geb selector in a Data-Driven Spock test - Sometimes it works

820 views
Skip to first unread message

David Muskat

unread,
Apr 6, 2015, 1:49:46 PM4/6/15
to geb-...@googlegroups.com
Hi,

I've written some functional tests using Spock and Geb. I'm using @Unroll and a 'where' clause to do data-driven testing. One of the data items in the Spock 'where' clause table is a Geb selector. This is so that when I loop through the test, I can reference different Geb selectors to click HTML buttons on the web page I am testing. I am using chromedriver.

I'm testing a page in my single page web app that has a question with 6 answers (the answers are labeled A,B,C,D,E,F). Clicking on an answer selects the answer. Using keyboard input by typing any of the letters  a,b,c,d,e,f,A,B,C,D,E,F should select an answer as well, and that is what this spec is testing.  To test that keyboard input still works when focus is not in the <DIV> containing the question and answers, I am clicking on a button outside of that <DIV> in the tests. I am also clicking on a button to navigate to the next page of the app.

Passing a Geb selector as a data item in the Spock 'where' clause (as shown below) does work. However, it seems that if I add more iterations to the test (by adding several more rows with Geb selectors as data items in the data table), I eventually get the stale web element error from Selenium when my test attempts to use a Geb selector. The error doesn't get thrown the first couple of times through, instead the error gets thrown at about the 4th or more times through when a Geb selector is referenced in the test.  If I modify my spec to pass a text String as a data item instead of a Geb selector, and then interrogate the text String in an if statement in the test to determine which selector to use, that always works.  I can do that, but its rather clunky instead of just passing a Geb selector as a data item. 

Any clue why passing a Geb selector as a data item in the Spock 'where' clause sometimes works?  I'm guessing its a race condition related to the number of loops through my Spock 'where' clause.

Here is my spec:

Unroll('App should accept #keypress key to select answer #answer after #event')
  def 'Make sure A,B,C,D,E,F key presses work for a,b,c,d,e,f answer letter selection'() {
    given: 'at question page with a,b,c,d,e,f answers'
    at(QuestionPage)
    waitFor {$('.question-container').displayed}

    when: 'A,B,C,D,E, or F key is pressed'
    app.answer(answer).displayed
    interact { sendKeys(keypress) }

    then: 'A,B,C,D,E, or F answer is selected'
    waitFor(30, 1.5){app.selectedAnswer.displayed}
    assert app.selectedAnswerLetter == keypress

    cleanup:
    report()
    if (selector != null)
      if (selector.displayed)
        selector.click()

    where:
    answer | keypress | event | selector
    //After page loaded
    '1' | 'a' | 'page loaded' | null
    '2' | 'b' | 'page loaded' | null
    '3' | 'c' | 'page loaded' | null
    '4' | 'd' | 'page loaded' | null
    '5' | 'e' | 'page loaded' | null
    '6' | 'f' | 'page loaded' | app.bottomToolbar.flag
    //After flag button clicked
    '1' | 'a' | 'flag button clicked' | null
    '2' | 'b' | 'flag button clicked' | null
    '3' | 'c' | 'flag button clicked' | null
    '4' | 'd' | 'flag button clicked' | null
    '5' | 'e' | 'flag button clicked' | null
    '6' | 'f' | 'flag button clicked' | app.bottomToolbar.next 


The Geb selectors above (app.bottomToolbar.flag and app.bottomToolbar.next) are defined in modules like this:

class BottomToolbarModule extends Module {
    static content = {
        toolbar { module ToolbarModule }
        next(required:false){ $('#bottom-toolbar-next') }
        flag(required: false, wait: true) { toolbar.button("#bottom-toolbar-flag") }
    }
}

class ToolbarModule extends Module {
    static content = {
        button(required: false, wait: true) { selector -> $("${selector}") }

    }


When I add more buttons to the table, that is when this doesn't work (stale web element), and I have to pass a String identifying the button in the table and interrogate that String in the test to determine which button to click, but that is rather clunky:

    cleanup:
    report()
    if (selector != null)
      if (selector == 'flag')
        app.bottomToolbar.flag.click()
      else if (selector == 'pause')
        app.bottomToolbar.pause.click()
     else if (selector == 'options')
        app.bottomToolbar.options.click()
      else if (selector == 'next')
        app.bottomToolbar.next.click()





Notice: The information contained in this message or any attached document is confidential and intended only for individuals to whom it is addressed. If you got this message in error, please inform me immediately using one of the methods above. In some cases, I may ask you to return the documents at my expense. In general, please simply destroy the information at once. Any unauthorized use, distribution, or copying of this information is prohibited.

Marcin Erdmann

unread,
Apr 7, 2015, 6:12:33 AM4/7/15
to geb-...@googlegroups.com
This doesn't work because "where" parameters are evaluated *before* executing a particular unroll iteration which means that they are not evaluated when you use them but way before. There are at least two ways of solving your problem I can think of. If all of these buttons live under the same content element (BottomToolbarModule in your case) then you can parametrize your spec using strings:

buttonName << [null, "flag", "pause", ...]

and then use it like this:
if (buttonName) {
    app.bottomToolbar[buttonName].click()
}

Another way is to use closures to be able to delay evaluation of these selectors. You can set closure delegate to the Browser instance of the spec to be able to use exactly the same code inside the closure as you would use in the spec if it was inline:
buttonSelector << [null, { app.bottomToolbar.flag }, {app.bottomToolbar.pause }, ...]

if (buttonSelector) {
    buttonSelector.delegate = browser
    buttonSelector.call().click()
}

--
You received this message because you are subscribed to the Google Groups "Geb User Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to geb-user+u...@googlegroups.com.
To post to this group, send email to geb-...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/geb-user/8c2f62be-aa56-4c7d-a200-38b257f3fdb7%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

David Muskat

unread,
Apr 7, 2015, 12:48:55 PM4/7/15
to geb-...@googlegroups.com

Marcin,

Thanks for the explanation about when those "where" parameters are evaluated. I also like your tidy way of using those strings. The closure idea looks interesting to try as well.

Thanks for the help!

-David Muskat   
Reply all
Reply to author
Forward
0 new messages