Select Mode

Why Test Engineers Should Learn Geb and Spock

Awhile back I talked about why test engineers should learn Groovy. Here I’ll focus on two specific tools in this ecosystem: Geb and Spock.

I should issue my standard caveat here, which is the idea that I do adhere to the idea there truly is no automated testing, just automated checking. Don’t become a technocratic tester just because you learn tools like this. And do keep in mind that automation language and development language do not have to be the same. That said, if you are working in a Java context but you want a more “modern” approach to script development, Geb and Spock are something you’ll want to look at.

For Rubyists like myself, transitioning to Java has always been a little bit difficult. Not because of the language differences, but rather because of all the boilerplate code Java forces you to put in place. Everything in Ruby is just so much cleaner. But when you start to get into Groovy, you see a dynamic language on the JVM that allows you to write very concise and expressive code.

If you want to play along, you’ll need the JDK installed as well as Groovy. I do use Gradle for one set of examples here so it won’t hurt to have that. On a Mac you can easily get Groovy and Gradle via Homebrew. There are plenty of instructions for installing Groovy and installing Gradle should you need help.

Please note this won’t be a full tutorial to Geb or Spock. What I am to do here is simply get you over what is a hurdle for most testers: simply getting started. What I want you to leave with here is working scripts and a feeling that you can explore more on your own.

Starting Simple with Geb

As most automated coders will tell you, when you are using libraries like Selenium or the WebDriver API, you rarely want to deal with the low-level details directly. Instead you want to use an abstraction of the WebDriver API. That, effectively, is what Geb does. Geb is a higher-level abstraction of the WebDriver API and combines the Groovy language with a jQuery-like selection and traversal API.

It’s a lot easier to just get to work showing things rather than describing it all. So let’s create a script called script.groovy. Put the following in it:

Grape is pretty cool, not least of which because it is built right in to Groovy. Incidentally, the above exclusion (@GrabExclude) seems to be necessary to get things to work. (See this issue.)

What the above is going to do is simply make sure that the relevant packages are available for this script. This is no different than how you would specify such dependencies in Ivy or Maven. And, in fact, later I’ll show how you can move this stuff to Gradle. With that in place, add the following to the script:

You can execute this via:

groovy script.groovy

I’m creating a new Browser instance. I then call a series of methods on that instance. If you’ve worked with Selenium in any language, my guess is the above should be fairly intuitive. The go method calls up a Firefox browser and navigates to the URL of my test application Decohere, specifically to the “Planet Weight Calculator” page.

I should note that Geb searches for available WebDriver implementations. With this script, Geb will find the previously loaded FirefoxDriver and will use it to start a new browser and connect to it. You can certainly use other browser drivers, like ChromeDriver, PhantomJS and so on.

The next few lines are basically ways to get a reference to particular elements on the page, supply some of those elements with data, and perform actions on those elements. One thing you might note is one of Geb’s core features in terms of how elements are selected. A syntax very much like jQuery, with CSS-style selectors, is used for selecting elements.

Speaking of being jQuery-like, if you don’t like using .find all the time, you can just use the $ character. So the above script could be modified slightly to this:

That cleans things up a little bit but you do still have to keep typing “browser”, though. Well, you can even get rid of that should you want to. There is a drive method that can be called on the browser itself. This method accepts a closure and, within that closure, all method calls will be delegated to the browser instance. So the script could become this:

There’s a whole lot of material that I could go over here but my goal is to just get you up and running with scripts as quickly as possible. I definitely recommend checking out the Book of Geb for lots more information.

Starting Simple with Spock

Spock is one of the more popular testing frameworks for the Groovy language. If you come from a Ruby context, while Geb was like Watir or Capybara, Spock is basically like RSpec. The nice thing is that Geb comes with good integration for Spock.

Spock provides a Specification class you integrate Spock with Geb by subclassing this class. So let’s practice with this. First we’ll add a few dependencies to our Grapes:

Now what you can do is replace all the existing code in your script (except for the grapes stuff, of course) with this:

If you run this — with groovy script.groovy — you’ll get:

No reports dir has been configured, you need to set in
the config file or via the build adapter.

What this is telling you is that you need to set a report directory in the config file that Groovy looks for which, by default, is called GebConfig.groovy file. If you create that file, you could put the following line in:

This is necessary because I’m subclassing with the GebReportingSpec. You could also do this:

Here I’m instead subclassing with GebSpec. If you look at the code for Geb, you’ll see that GebReportingSpec extends GebSpec and one of the reasons you might use the former rather than the latter is because GebReportingSpec automatically creates screenshots when test failures occur.

Geb with Gradle

Now let’s use Gradle. Up to this point, it didn’t really matter where you stored the script but once you get into build tools like Maven or Gradle, you can make your life a lot easier if you follow the conventions. So create some project directory (i.e., learn-geb). Within that directory, create a file called build.gradle and put the following in it:

Here you’ll notice that the dependencies section is basically including what we were previously including via the Grape dependency manager.

Within your project directory create the following path: src/test/groovy/specs. In that directory, create a file called PlanetWeightSpec.groovy and put the following in it:

You can execute this by running the following command:

gradle clean test

That should work just fine because, if you notice, it’s basically the exact same bit of logic we were using without Gradle.

Page Objects

Most likely if you’re reading a post like this, you already know the idea behind page objects. The core idea is that each page of the web application is represented by an object The information on a page, as well as the possible user interactions involving elements on the page, are described by the properties and methods of the corresponding page object.

Geb provides support for the page object pattern via the Page class. The elements and possible actions to be taken on a page are described using a domain-specific language.

Now let’s use the page objects. In your project directory, create the following path: src/test/groovy/pages. Within that directory, create a file called WeightPage.groovy. Then put the following in that file:

With that in place, let’s modify our spec:

A simple change initially. What you should notice is that Geb provides a few methods, like at, which is used to verify whether a script is on the correct page and to, which is used to redirect the driver to the page by using the url defined on the page object.

Now let’s add to our WeightPage.groovy file:

The content property is a closure that defines — well, content. Specifically, the content of the page that this page object is modeling. The content can be anything on the page that you want a simple accessor for but it can also be methods that represent actions taken on the page. Lines 10 to 12 show you where I’m defining element definitions, meaning I’m giving a friendly name for elements that I’m identifying via the provided selectors.

Lines 14 to 17 show you a method I created that will take in a value and then use those friendly-named elements to perform actions — entering text in a text field and clicking a button in this case. Line 19 shows another method that simple returns a value from a provided element.

Now let’s update our spec script to use these new elements:

Once again, run gradle clean test and you should see everything working.

Take some time to study the page object and the script and make sure you understand why is working as it does.

The Java Ecosystem

Geb and Spock are finding a bit more traction in the development space. Unfortunately, not many companies are actually embracing these tools quite yet. There is still a focus on so-called “pure” Java approaches, using Selenium directly. I created a Java-based test framework repository to experiment with this stuff myself and I’ve now done the same thing with a Groovy-focused approach.

As a technical test engineer, particularly one that has chosen to generalize among many languages, the Java ecosystem offers a lot of promise but also a lot of challenge. The best thing I’ve found is to be experiment enough such that you aren’t nervous about getting started with each bit of technology.

Share

This article was written by Jeff Nyman

Anything I put here is an approximation of the truth. You're getting a particular view of myself ... and it's the view I'm choosing to present to you. If you've never met me before in person, please realize I'm not the same in person as I am in writing. That's because I can only put part of myself down into words. If you have met me before in person then I'd ask you to consider that the view you've formed that way and the view you come to by reading what I say here may, in fact, both be true. I'd advise that you not automatically discard either viewpoint when they conflict or accept either as truth when they agree.

10 thoughts on “Why Test Engineers Should Learn Geb and Spock”

  1. Interesting……….I’m coming from the other way around: from C# to ruby……i don’t like it (ruby that is)……i find Ruby waaay too slang, too vernacularish, way too casual………I prefer my words clearly spoken and enunciated, prose that is explicit and cleanly spoken 🙂

    1. Yep, you’re probably not alone in that.

      Vernacular, of course, is “common” — and I do prefer my language constructs to be common, but that’s also because I’m not an enterprise developer: I’m often a test solution developer. And test solution code should follow domain-driven design, which does in fact mean you want the business vernacular to be reflected in code.

      My focus is not so much on the language itself rather than the structural aspects around it. I prefer dynamic languages for test solution development, which means, to me, Groovy and Ruby are very much equivalent in what they allow me to do.

      But I could say the same for Java and C#.

      So when you say you prefer the “clearly spoken and enunciated, prose that is explicit and cleanly spoken”, and then say C# may be that while Ruby is not, I think you are making something orthogonal that is in fact not. About the closest you can get with this would be with either DSLs or fluent APIs — and you can do those in any language; it’s just a matter of how much boilerplate you want (or are willing to tolerate) around it.

  2. So grateful for this post, thanks!

    BTW, rather than Homebrew, I used to use MacPorts on OS X — till sdkman.io came along. Super easy tool to manage my installations of JDK, groovy, grails ,etc.

    Also BTW, I kept MacPorts for only one thing at the end: Postgresql. Now: docker! So much cleaner!

  3. May I know how do we call a function form one class to another in geb?

    Class1:

    when:
    def “test”()
    …..

     

    Class2:
    given:
    def cls1 = new Class1
    cls1.”test'()

     

    Above didn’t work

    1. I’ll be honest with you, I don’t know. I haven’t had much opportunity to play around with Geb and Spock compared to other solutions. The particular of method you’re trying to call there — the “test”() — is referred to as a feature method. As such, they generally hold state and context for the situation they are in. So calling one from another class could be an interesting exercise. I say that because calling a feature method means you are also calling its operative context. I haven’t seen many situations where I would want to do this. (Which is not me saying such situations don’t exist.) If I find myself wanting to call a feature method, I would rather look at abstracting out the bits of that feature method into a helper method that both classes could rely on.

      That said, I’ll do a little investigating and see what I can dig up.

  4. Jeff the helper method is definitely the way to go in that situation. They are more like the standard methods we all know. The feature methods are intended to appear in logs and reports like a description of what part of a test is being executed. You could make the whole body of the feature method a method in a helper class if you felt like it and that would keep things clean.

    1. Well not the whole body, the bits that do stuff. So:

      given “something”
      when “some operation”
      call helper method to do the op
      then “some expectation”

      I’d also point out that a feature method is kind of a full test so you can have:

      given

      when
      then

      when
      then

      and so on.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.