Our favourite test automation software in the world still remains Cucumber and its Gherkin syntax, but like all software, it has its limitations. In particular, at first glance it is pretty difficult to express workflows in Cucumber that contain branches. In this post, we explain how that can be achieved.
Table of Contents
For more information and useful articles about Test Automation, read our Test Automation Guide.
The Problem with Branches
Imagine your software workflow is such that depending on some criteria, your tests should follow a different path through the software. The following flow chart is supposed to provide a very much simplified example of this.
This simple workflow takes into consideration where a client connects from, and offers a different path through the software towards reaching the workflow goal depending on that information. In this case, the different path is represented by an additional payment option based on whether the client’s IP address is located in the UK or not..
In Cucumber, each path needs to be a separate test scenario, branching off onto a different path is simply not something Cucumber supports.
First Scenarios
Let’s ignore the special case for UK clients for the moment and look at how we might phrase the other scenario in Gherkin.
Feature: Checkout
Scenario Outline: International checkout
Given I am on the checkout page
When I enter my email address
And I enter my password
And I select
And I provide
Then I expect the payment details to be accepted
And I expect to see an order confirmation page
Examples:
| payment method | payment details |
| credit card | my CC number |
| paypal | my paypal email |
The example is particularly powerful if phrased as a scenario outline in that it already gives you a hint for how simple branches might be captured in Cucumber/Gherkin, use scenario outlines and example tables.
The above example is also rather artificial in nature, because a credit card checkout process typically requires you to provide not just the credit card number, but also the cardholder’s name, and a verification code. In some cases, it might also include an additional verification step after that. Clearly the example given here does not capture all your needs.
For the sake of the argument, though, assume that it does.
Reuse for the Second Scenario
For the second scenario, we can reuse a bunch of the first scenario from above. For starters, the beginning lines will be identical.
Given I am on the checkout page
When I enter my email address
And I enter my password
But also, the end should be entirely the same:
Then I expect the payment details to be accepted
And I expect to see an order confirmation page
Our problem with branches can be seen from a different perspective, then: what is it that is reusable about all the scenarios and what is it that is different?
A Solution to Branches
If viewed in this light, our problem isn’t so much about dealing with branches, it’s about how to best reuse existing steps. The first thing to do is to adopt the best practice of “push how down”, which refers to having less detailed steps, and leave the detail up to the step definitions.
With that in mind, our starting block can be rewritten like this:
Given I provide my credentials on the checkout page
And the second part might become:
Then I expect the checkout process to complete
The good news in all this, is that you don’t have to write more complex step definitions. Instead, you can reuse existing steps.
Given /^I provide my credentials on the checkout page$/
step "I am on the checkout page"
step "I enter my email address"
step "I enter my password"
end
# Or perhaps more intuitively:
Given /^I provide my credentials on the checkout page$/
steps %Q{
Given I am on the checkout page
When I enter my email address
And I enter my password
}
end
Unfortunately, error reporting from this kind of aggregate step isn’t as good as from a more complex step. The recommendation is, instead of using and calling steps, you should just use and call Ruby methods:
# features/support/env.rb
def go_to_checkout_page
end
def enter_email
end
def enter_password
end
# features/step_definitions/something.rb
Given /^I provide my credentials on the checkout page$/
go_to_checkout_page
enter_email
enter_password
end
While this is pretty good for fairly single-purpose pieces of functionality, when you want to reuse functions across many scenarios or even test suites, it is usually better practice to bundle them into modules. It is also better practice not to pollute the global namespace; fortunately Cucumber provides for that:
# features/support/some_module.rb
module SomeModule
def go_to_checkout_page
end
def enter_email
end
def enter_password
end
end # SomeModule
# features/support/env.rb
require 'some_module'
World(SomeModule)
The step definitions remain the same, but putting functions into modules lets you organize them better and keep them from conflicting in the global namespace. Invoking World(SomeModule)
extends the Cucumber World object with the functions from the given module (or modules; multiple modules are possible). The World
object is passed to each step definition as self, meaning all its functions are immediately available for the step definition to use.
Conclusion
Aside from the advantage that “push how down” provides the reader with more of the intent of a test scenario over it’s mechanics, fewer more powerful steps also means it’s easier to capture when workflows branch.
To deal with branches, examine your workflows for blocks of step definitions that always occur in the same order. Once identified, those step definitions are prime candidates for folding into one, more complex step.
Additionally, instead of writing code directly in step definitions, think about reusable pieces of functionality, and bundle them in modules that you extend World with.
- Small differences may be best captured in scenario outlines and example tables.
- Larger differences are fairly easily dealt with by using fewer, more complex steps.
- Assembling complex steps become easier when their common functionality is bundled in modules.
The only situation not captured in these guidelines are workflows of such complexity that they must necessarily include lots and lots of branches. If you find yourself confronted with workflows of that complexity, it’s very likely that parts of the workflow can be separated out as Sub-Processes. Also, workflows can also get complex when testing in Scrum teams which use the Gitflow worklflow. Doing so will yield workflows more easily testable separately and, if the above guidelines are adhered to, result in a number of Ruby modules you can re-use in your tests.
If implementing this seems daunting and you think your organisation could use some help, we offer test automation consulting services which can help you setup an automation suite or allow you to outsource the entire process to us.
You could also learn from our experience with Cucumber and Gherkin by using our guide on this open-source test automation software.
Calliope Pro test results dashboard
Workflows with Cucumber are not the only things that can get complex. Performing automated tests can quickly become unmanageable without maintaining a clear overview of your test results and keeping your development team informed. Calliope Pro is designed to help you manage your test results by storing them in one secure location, enabling you to share test results (with screenshots) between team members, and also schedule and run tests on demand. Calliope Pro is tools agnostic, meaning it will work with any testing tools or frameworks you use.
Give it a try for free. No credit card is required for this free starter account!
Suggested Posts



The 3 most common mistakes writing Gherkin features
In the past, I’ve worked on many projects where they either used Gherkin to describe functionality or to be used for test automation. However, my



Set up and run your Cucumber and Selenium web tests on Gitlab-CI within minutes
Build great software faster! CI/CD is hot and is still gaining interest. Test Automation is obviously essential within any Continuous Integration setup in order to



Complete Setup Guide for Ruby, Cucumber and Watir on Windows
Aside from the tools explained in this post, we have recently launched a test results dashboard designed to help you maintain an overview of all