Writing Good Gherkin Enables Good Test Automation

Writing Good Gherkin Enables Good Test Automation

Behavior Driven Development, also known as BDD, is an example-based methodology for developing software. The key to BDD is collaborative exercises between Business, Development, and Testers. These parties are collectively known as the Three Amigos.

When beginning the work for a new feature, the three amigos get together and write examples of how this feature will be used. By discussing these examples, all parties come to a shared understanding of how the feature should behave in various scenarios.

Let’s take a peak into one of these meetings. [If you’d prefer to watch me do this on video, I have it recorded.]


illustration of three team members in a conference room sitting around a table

Three Amigos Meeting

Parabank team is building a new feature which will allow users to withdraw money from their account. The three amigos begin to envision this scenario and how the application will behave.

They use a Gherkin syntax which allows them to use domain-specific language in a Given, When, Then format.

The Given-When-Then syntax forces them to think about the current state of the system (that’s the Given), the action that is taken on the system (that’s the When), and the resulting behavior of the system (that’s the Then).




The three amigos decide to draft their first scenario, which is a happy path where the user withdraws from his bank account.

They consider the prerequisites that determine the current state of the system before any action is taken. They decide that they need:

  • A customer who has an account
  • A certain amount of money in that account, let’s say $100




So they write this in Gherkin format by using two Given statements:

Notice here, the use of the word And. This keyword is used when there’s more than one of a specific type of statement. As opposed to saying “Given X, Given Y”, it’s written in a conversational tone: “Given X And Y”.

Now that the team has decided on prerequisites, they describe the action to be performed. This is described using a When statement:

Finally, the team discusses how the system should behave in this situation. This is done via two Then statements:

Now, the team has a complete scenario described and the three amigos know what it is they need to build.


illustration of customer at bank. Given, When, Then statements overlayed

Benefits of BDD

Practicing BDD has many benefits. Team members are able to collaborate and gain a shared understanding before development begins, which means any ambiguity or differences of opinions can be discussed very early in the process and addressed before any development begins.

Everyone is speaking the same domain-specific language so there’s no confusion about things like terminology.

If written in Gherkin syntax, the scenarios can be used as executable artifacts that drive automated testing of those scenarios.




Using Gherkin for Test Automation

The Gherkin-style scenario can be added to what’s called a feature file. This is an example of the feature file and it serves as input into test automation scripts.

However, many teams get stuck here because a lot of the details about how to execute this scenario are missing.

It says a customer has an account. But which customer? And what’s the account information?

It says the customer withdraws ten dollars but doesn’t give the steps on how that’s done? What pages do I need to go to and what UI elements do I need to click on?

It describes the expected result but not where to go to verify that.



feature file that is describing every single action (click this, click that)Because of this, often times, testers totally rewrite that concise feature file into something like this.

Yes, this provides a lot more detail but there are some cons to this approach.

In addition to reducing the reusability of the steps, these steps contain a ton of implementation detail and explicitly dictate how the automation should be written.

One reason that the three amigos come up with scenarios that focus on behavior and not implementation, is because how you make this happen isn’t really relevant, and by adding all of this detail, you’re losing the intent of the scenario’s behavior.

So, if we don’t provide implementation detail for product development, we certainly shouldn’t do it for automation development either.



Back to our concise scenario that describes behavior. Yes, a lot of detail is missing here. But that’s ok. In fact, it’s great!

You may ask, well how will we know how to implement this?!

We leave that to whomever needs to write the automation code. Just as we trust the developers to be able to develop the feature without the implementation details, we can do the same for those developing the automation code.

Let’s take each one of these steps and see how we can turn this into glue code that actually executes the scenario and also follows good test automation design principles.



Writing the Test Automation (Glue) Code

Using Cucumber, the automation engineer takes the scenarios from the three amigos meeting and places them in an executable feature file. Here’s the one that she will work on:

The automation engineer creates a Java file to hold the automation code. The code in this file is called step definitions and they map to the steps within the feature file above.

She also adds a runner file to tell Cucumber where the feature files and step definition files are.


Step 1

Now she’s ready to tie the steps from the feature file to the glue code! She takes the first step:

and adds glue code in WithdrawStepDefs to map to it. This is done by using the Cucumber annotation @Given followed by the text from the feature file.

Immediately following this annotated glue step is the method that should be executed when this step is called. This method can be named anything and can execute any code the automation engineer desires.

Again, based on this step, she doesn’t know which customer this is or anything about the account. That’s ok. She can decide on a customer for this scenario and  push the details of that customer down to a lower level versus storing it inside of the feature file. She decides to add the customer’s information to a properties file. She already has one that stores the url of her application as well as the location of her chromedriver executable, so she simply adds a new section for the customer details.

In order to do anything with this account, the customer needs to be logged in. Again, not something that should be placed in the feature file, as it’s needed for just about every scenario! So, this can also be pushed down to the code.

The automation engineer decides to place this in a separate Java file so that she only has to implement it once and that every test scenario can automatically execute this by using inheritance:

She also updates the step definition file to extend this BaseTests class

So, now login is taken care of. Time to decide on the account. It’s a good thing that the account details were not specified in the feature file. This allows the automation engineer to follow good practices. She doesn’t want to use an account that already exists because if there are more tests that are running in parallel and they are also accessing this account (and expecting a certain amount to be there), this will cause a clash with the test data and the tests will fail. So, she decides to create a new account on the fly, and because the implementation details were not in the feature file, she can do this.

Another decision the automation engineer makes is to utilize her application’s web services to create this new account. Creating an account is not a part of this test; it’s a pre-req. There’s no good reason to do this on the UI which will take much longer. Again, because those click steps were not dictated by the feature file, she can choose the best way to implement this. She uses the Rest-Assured tool to make the web service call and to parse the response.

Note: The other source code files used here can be found at the Github link at the end of this article

This covers the first step of the scenario.


Step 2

Let’s move to the next step.

Now the automation engineer needs to ensure that there’s $100 in the new account that she created. Again, this is something she decides to do at the service level to keep her test nice and fast.

Notice on line 1, she uses a wild card (*) for the amount. This makes the step reusable for future scenarios. Because there is a wild card, the associated method must accept a parameter for that value – this is represented by the variable desiredBalance. Look at how efficient she can be since the exact steps were not dictated to her via the feature file.


Step 3

The next step is also one that doesn’t necessarily need to be done on the UI! Thank goodness the step only tells her what needs to occur and not how to make it happen. The automation engineer decides do this via a web service call as well.


Step 4

What’s next?

Then statement. Which means it’s time to verify. The automation engineer needs to make sure the account now holds $90. Another web service call to the rescue!


Step 5

Final step. Another verification point.

The automation engineer debates this one. She can essentially do this one with a web service call as well, but she knows it’s also important to make sure that the transaction record is showing to the customer how it’s supposed to and that none of the details are in the wrong places.

Here’s how the UI looks when everything is working.

There’s a LOT of details on this page. The automation engineer wants to make sure that everything is correct: the total balance, the available balance, the account number, the account type, the transaction records, etc.

She’s not excited about writing all of these assertions. Then it hits her…she doesn’t have to! Because all of this wasn’t detailed in the feature file, again, she can make wise choices and go for a more efficient approach. She decides to use the Applitools Eyes API to do a single visual assertion which will verify a screenshot image of the entire page. That way she covers all that she needs to verify with less code.

Because some of the data on the page will change each time the test runs (account id, dates of transactions), she simply annotates those areas using the Applitools dashboard so that the visual check knows those areas are dynamic.

Good Gherkin, Good Automation

So there we have it. Because the automation engineer took the Gherkin file from her three amigos meeting and didn’t modify it to add all of the implementation details, she was able to work smarter when turning it into test code. She’s able to use the UI when she needs to, API when she needs to, database calls and anything else that would make the tests more efficient while following good test automation practices.


See Me Code This


See Full Solution on Github

Angie Jones
  • Stella Nkirote M'Mukindia

    A great article with clear info and easy to understand. Thanks Angie for your endless feeds which are really helpful. You will always be my Techie Hero!

    September 16, 2019 at 2:34 am Reply
  • Pramit Singh

    Nailed it !

    September 17, 2019 at 8:45 am Reply
  • Jitendra

    Great article Angie.

    September 20, 2019 at 7:20 am Reply
  • Avinash Shetty

    Great article. Simple, clear and crisp. @Angie

    October 11, 2019 at 6:41 am Reply
  • Biswajit Pattanayak

    ” Just as we trust the developers to be able to develop the feature without the implementation details, we can do the same for those developing the automation code.” – This is probably my takeaway from the article. So simple statement, yet so powerful. I have been making arguments against writing procedural statements under the garb of BDD at all places, now I can make use of this powerful argument.

    But there is another problem I face at times. Teams ask the recipe to write perfect BDD scenario. I respond that it is a process which we need to retrospect time to time. A team starting with BDD can’t write perfect scenario on Day1. But I have not been able to convince some set of traditionally manual testers who are transitioning to BDD. Do you have any suggestions on this?
    October 13, 2019 at 9:27 am Reply
  • Richard Forjoe

    Hi @Angie, Thanks for this, really useful as usual. I was wondering if you could help. I’m struggling to amend the ServicesUtils class to include a body for the the POST method.

    Any chacne you can expand on the structure on the ServiceUtils class and how to amend it to add a body, parameters and headers to the request eg POST
    private static RequestSpecification request = given ().accept(ContentType. JSON ).body(); – Do i need to use this for the POST if it includes a body?Thanks in advanceRichard
    December 18, 2019 at 2:23 pm Reply
  • Vinay

    Thanks Angie, Great article

    June 19, 2020 at 3:05 am Reply
  • Luis V.

    This is amazing!!, thanks a lot for sharing

    October 12, 2020 at 9:13 pm Reply

Post a Comment