Overview

Browser Automation and Acceptance Testing with Geb

16 Comments

This post focuses on the technical side of automated acceptance tests for web applications. There are a lot of high-level frameworks, that allow definition of acceptance tests in natural language (Robot, JBehave, Cucumber, …). But when it comes to the technical implementation of the test cases, you are often forced to use the rather low-level WebDriver API directly.

Geb addresses exactly this problem. It is an abstraction of the WebDriver API and combines the expressive and concise Groovy language with a jQuery-like selection and traversal API. This makes the test implementation easier and the code more readable. On top of that, you get support for the page object pattern, asynchronous content lookup and a very good integration in existing test frameworks.

Quickstart with the Groovy Shell

If you want to start playing around with Geb immediately, just open up a Groovy Shell (normally via groovysh) and type in the following lines. Of course, you can also save these lines to a file and execute it with groovy. But for trying out new things, I think the Groovy Shell is a good starting point.

import groovy.grape.Grape
Grape.grab(group:"org.gebish", module:"geb-core", version:"0.9.0-RC-1")
Grape.grab(group:"org.seleniumhq.selenium", module:"selenium-firefox-driver", version:"2.28.0")
import geb.Browser
 
browser = new Browser() 
 
// the duckduckgo website has a much cleaner structure than google
// that makes it a better choice for demonstrating browser automation
browser.go "https://duckduckgo.com/"

After loading the necessary libraries with Grape, a new Browser instance is created. At this point, nothing else happens. When calling the go-method, a firefox opens up and navigates to “http://duckduckgo.com”. In the background Geb searches for available WebDriver implementations. It will find the previously loaded FirefoxDriver and will use it to start a new browser and connect to it. Now we can control the firefox via the Browser instance. For example, we can search for the term “Groovy Browser Automation” and click on the first result.

// fill the search input field with the searchterm 
// ".q" is just a shortcut for find("input", name:"q")
browser.find("#search_form_homepage").q = "Groovy Browser Automation"
 
// click the button with id "search_button_homepage"
browser.find("#search_button_homepage").click()
 
// click the first "a"-tag below the element with class "links_main" 
browser.find(".links_main").find("a", 0).click()

In this little example, we already see one of Geb’s core features: the jQuery-like syntax for selecting elements. You can use a great variety of CSS-selectors and attribute-matchers, you can filter the result-set or find descendants. And you can easily iterate over the result-set (like we do in the next example). For more information on element selection and traversal, have a look at Geb’s reference documentation (the “The Book of Geb”). It is not only a complete overview of Geb’s features (including configuration and integration in test frameworks and build systems), but also a good starting point for anybody who wants to learn more about Geb.

When using Geb outside the Groovy Shell, it is probably not very convenient to type “browser” over and over again. Thankfully, Geb offers a shortcut: the drive method accepts a closure, where all method-calls are delegated to the underlying Browser instance. In the next example, we extract the items of the sidemenu from the Geb-Homepage. We also show another handy shortcut: instead of find, we can use $. Those already familiar with jQuery, probably aren’t too surprised by this choice.

browser.drive({
    go "http://www.gebish.org/"
    $(".sidemenu a").each({
        element -> println element.text()
    })
})

You have seen, how easy it is to do browser automation with Geb. In simple scripts, it is enough to create a Browser instance and use its drive method. But Geb is much more than a helper for browser automation scripts. So let’s have look at something more interesting: Testing.

Integration with the Spock Testing Framework

Spock is one of the most popular testing frameworks for the Groovy language. And Geb comes with an excellent integration for Spock. I won’t go through the details of configuring Geb and Spock in a maven (or gradle) project. Detailed instructions, how to do this, can be found in the Book of Geb. I can also recommend the geb-maven-example on GitHub.

The Spock integration of Geb comes with a subclass of Spock’s Specification class. Inside test methods, you have the same possibilities as when using the Browser.drive method. You have access to a Browser instance via the browser property. Geb takes care of configuring and starting the browser before the tests (as well as closing the browser once the tests are finished). Just like inside a drive block, you don’t have to use the browser property. All unknown methods are directly delegated to it. This makes the tests very clean and readable. As an example, the following test makes sure, that the Geb homepage is the first result when searching for “Groovy Browser Automation”.

import geb.spock.GebSpec
 
class SearchSpec extends GebReportingSpec {
    def "search 'Groovy Browser Automation' in duckduckgo"() {
        given: "we are on the duckduckgo search-engine"
            go "http://duckduckgo.com"
 
        when: "we search for 'Groovy Browser Automation'"
            $("#search_form_homepage").q = "Groovy Browser Automation"
            $("#search_button_homepage").click()
 
        then: "the first result is the geb website"
            assert $("#links").find(".links_main a", 0).attr("href") == "http://www.gebish.org/"            
    }
}

By default, Geb searches the classpath for an available WebDriver. This is enough, if you just want to play around. In a real-world-project however, there are more things to consider. In the CI environment you’d want to run the tests in a headless mode (for example using PhantomJS and GhostDriver). If you’re developing a ROCA application, perhaps you also want to check how your application behaves when JavaScript is disabled. Then HtmlDriver is a possible option. This can be achieved by defining different profiles for Geb. Just place a file GebConfig.groovy inside your classpath and Geb will automatically read profile definitions from there. The profile itself is then determined by the system property geb.build.profile. Take a look at my geb-demo project on GitHub to see profiles in action. The project’s README file also contains detailed instructions, how to execute the tests with maven and how to set up the necessary infrastructure (e.g. a PhantomJS daemon).

Tip: Geb provides yet another base class for spock-tests: GebReportingSpec. The only difference to GebSpec is, that GebReportingSpec automatically takes a screenshot for failed tests. This can be very helpful when you search the reason for failing tests.

The Page Object Pattern

Tests are often treated as second-class citizens. But the same principles, that adhere to production code, can and must be applied to test-code. “Open/Closed” and “DRY” are such principles. And the Page Object Pattern is a means to observe these two. The idea behind page objects is quite simple: Each screen (page) of the web application is represented by an object (page object). Similar pages are represented by objects of the same class. The information and possible user interactions of a page (or a class of pages) are described by the properties and methods of the corresponding page object (or its class).

Geb provides support for the page object pattern via the Page class. The elements and possible actions of a page are described inside the content block using a simple DSL. To illustrate how this works, we take the movie database application of Tobias Flohre’s recent blog post on ROCA and write some acceptance tests. The code of the following examples (including maven configuration and README) can be found in my geb-demo project on GitHub.

One can quickly make out candidates for page objects in the movie database: The initial page could be labeled MovieListPage. When you click on a movie, the details to this movie are displayed (MovieDetailPage). And a click on the “Edit” button leads to a simple form to change these details (MovieEditPage). The implementation for the MovieListPage could look like this:

import geb.Page
 
class MovieListPage extends Page {
    static content = {
        searchform { $(".form-search") }
        searchSubmitButton { searchform.find(type: "submit")}
 
	// this defines a method "searchFor" that takes a single argument
	// the search-form is filled with the given argument and the search-button is clicked
        searchFor { searchTerm ->
            searchform.searchString = searchTerm
            searchSubmitButton.click()
        }
 
	// required=false is needed, because otherwise Geb would automatically throw
        // an AssertionException when the method returns nothing
        movie(required: false) { movieName -> $("td", text: movieName).parent() }
        containsMovie(required: false) { movieName ->  movie(movieName).present }
 
        movieCount { $("#pageContent tr").size() }
    }
}

This definition of the MovieListPage is now the base for tests of the movie database’s search feature. In the following example, we navigate to “http://localhost:8080/moviedatabase”. With the at-method, we tell Geb, that the current screen should be represented by an instance of MovieListPage. After doing so, the Browser automatically delegates all unknown method calls to this object. And because GebSpec delegates all calls to the underlying Browser instance, we can directly call any method of the MovieListPage (e.g. the searchFor method to execute a search). At the end of the test, you can see one of Geb’s other nice features: asynchronous content lookup. Because the search result is loaded via ajax, there is no full page reload. So we tell Geb to wait up to 5 seconds (the default timeout) for the search result to show up. If the movie list doesn’t contain “Star Wars” after 5 seconds, the test fails.

class SearchSpec extends GebSpec {
    def "search for 'star' contains movie 'Star Wars'"() {
        given: "we are on the movie database homepage"
            go "http://localhost:8080/moviedatabase"
            at MovieListPage
 
        when: "we search for 'star'"
            searchFor("star")
            at MovieListPage
 
        then: "the search result contains 'Star Wars'"
            // the waitFor is needed because the movie-database uses javascript
            // heavily and the click on the searchbutton doesnt trigger a page-reload
            waitFor { containsMovie("Star Wars") }
    }
 
    def "search for 'foo' returns empty result"() {
        given: "we are on the movie database homepage":
            go "http://localhost:8080/moviedatabase"
            at MovieListPage
 
        when: "we search for 'star'"
            searchFor("foo")
            at MovieListPage
 
        then: "the search result is empty"
            waitFor { movieCount() == 0 }
    }
}

Conclusion

Geb is not only a very nice DSL for browser automation, it has also many powerful features. Most notably the jQuery-like selection API and the support for the Page Object Pattern. Another highlight is the asynchronous content lookup. There is support for existing test frameworks like Spock, easyb, JUnit and TestNG. Integration for JBehave or cucumber-jvm is also possible with the BindingUpdater. Getting started is easy and the documentation answers questions before they arise. There is no reason why you shouldn’t give it a try ­čśë

Kommentare

  • Sam

    Looks nice. I’m afraid I’ve got my own WebDriver wrapper:
    http://code.google.com/p/redrobot/

    I’ve been looking for a “contrib” respository for WebDriver but I’ve not been able to find one.

    It would be nice to pull together.

    Cheers

    Sam

    • Michael Lex

      13. February 2013 von Michael Lex

      Hi Sam,
      using fuzzy matching for element selection sounds interesting. As for the WebDriver “contrib” repository: I am not a committer of Geb, but I think the mailing-list would be a good place to announce the idea.

      Greetings,
      Michael

  • Bobby

    6. August 2013 von Bobby

    Does Geb support Safari, Opera, and parallel testing?

  • Michael Lex

    12. August 2013 von Michael Lex

    Geb supports anything that WebDriver (aka Selenium 2) supports. This includes Opera, Firefox, IE, and others (see the WebDriver FAQ for a more complete list).

    As for parallel testing, this depends on the test runner. If you use the spock framework and the maven surefire plugin, you can configure it to run the tests in parallel.

  • Vish

    Can we execute the UI test cases by using Geb and junit in eclips?

    Can you please suggest me the way .. how to do this?

    • Vish

      Can we use Geb for a Java application ?

      • Michael Lex

        1. October 2013 von Michael Lex

        Yes, of course. Geb is a Groovy library, but you can use it for any application you want.

    • Michael Lex

      1. October 2013 von Michael Lex

      Geb itself is only a library to support testing. You need some other test-framework on top of it. If you use JUnit or Spock (which comes with a JUnit-Runner), executing the tests from eclipse should work out of the box.

  • Saikat

    Michael,
    Thanks for a lot of info about GEB and groovy. I need some help to understand about the Spock framework with Groovy for AT automation. I am new in this language. I know basics of JAVA. Can you please help me with providing the tutorials? Or is there any way you can help me to learn automation using GEB with groovy?

    Thanks.

  • Anshu

    Geb( with Spock) supports Google Chrome 32?

  • Siva Kanukollu

    7. March 2014 von Siva Kanukollu

    Hi All,

    I would like to evaluate Geb with Spock for my project requirements. I am confident we can automate web browser testing with Geb. My other requirement is to automate web services (soap & rest). Does any one tried testing web services with Geb. If yes, please share your experience, it would be a great help for me.

    Thanks
    Siva

  • Saket

    14. March 2014 von Saket

    Micheal,
    In my current project i am stuck while using GEB for automation and i need to discuss with you privately for those. Can you please send me your email so i can forward you the code and get some advise from you.

    Thanks.

  • Reetu

    5. May 2014 von Reetu

    Hi,I have a main menu with 4 options when i mouse over on main menu i will get a sub menu, here i have links to click. I would like to know do we have any option to mouseover to the main menu and verify the sub menu option. Here is the HTML code

    Please suggest me how to write the code for this.
    Thank you,

    • Michael Lex

      19. May 2014 von Michael Lex

      Hi Reetu,
      it depends on the way you implemented the dropdown-menu. You can try the WebDriver moveToElement action (which should be available in Geb, too).

      If you use jQuery, you can also use the jQuery integration of Geb to force the browser to display the sub-menu explicitly with $(‘#menu’).jquery.show().

      Greetings,
      Michael

Comment

Your email address will not be published. Required fields are marked *