In last week’s post, we provided a complete setup guide for cucumber and related packages on Windows. This week, we’re going to cover the basics of cucumber. You’ll learn about what each file in a test suite means, and you will write test scenarios in Gherkin structured English.
What’s a Testsuite?
Here at spriteCloud HQ, we tend to discuss collections of test scenarios we call “test suites”. The term isn’t particularly unique to us, but it may be useful to illustrate what we mean by it in relationship to cucumber.
Cucumber provides a flat hierarchical structure for test scenarios. At the top level you will find what cucumber calls “features”. Features in cucumber have a name and a description, and contain scenarios as their direct descendants in the hierarchy.
Depending on the application you’re testing, and the team you’re working with, the notion of structuring test scenarios into “features” may not be the best fit. After all, features can have arbitrary complexity! Just compare a relatively simple newsletter “feature” to a highly complex online shop “feature”. From a certain point of abstraction, both are just features of equal gravity, but when developing and testing each, the online shop will quickly turn into a much more complex beast.
In our opinion, it’s better to think of cucumber “features” as something akin to a user story, and we would recommend phrasing the cucumber feature’s description in those terms.
Here’s a quick example:
Feature: Example Feature In order to become productive As a test automation engineer/product manager I want to understand the basics of cucumber
Here, the name of the feature is what follows the colon in the first line, so “Example Feature”. The description is made up of the indented lines below. This format is part of the Gherkin structured English language. Gherkin is easy to learn, and you’ll be introduced to it as part of this series. But for now you should note that it takes indentation seriously; the description must be indented — but by how much is up to you.
With this in mind, a test suite in cucumber is really a collection of user story like features and as such should only contain user stories that relate to the same system. Cucumber expects each feature to live in its own file, and looks for those files in a folder called
features, so a different way of thinking about it is that a test suite is the collection of all the feature files in a folder.
So how would I go about organizing those files, precisely?
Recap: A New Testsuite
Let’s recap quickly how to create a new test suite. It’ll already demonstrate some of cucumber’s expectations with regards to folder structure, and we can build on that.
To begin with, let’s create a new folder for our test suite.
C:\Users\you\Documents> mkdir "My Testsuite" C:\Users\you\Documents> cd "My Testsuite"
In the following examples, we won’t show this path any longer. Suffice to say that we expect all command line stuff to happen in the
C:\Users\you\Documents\My Testsuite folder, unless otherwise specified.
Now let’s create a test suite again.
C:...> cucumber --init create features create features/step_definitions create features/support create features/support/env.rb
The first thing you’ll notice is the
features folder cucumber creates. In here, we’ll collect our feature files.
Below this folder, cucumber also creates an empty folder called
features/step_definitions, and a folder called
features/support, which contains an empty text file
Files ending with
.rb are Ruby files. The
env.rb file in this location is the first Ruby file cucumber tries to load when it runs. Later on, you can put any code in it that needs to be executed at the start of your test suite. Try it out by putting the following Ruby code into it:
puts "Hello, world!"
If you then run cucumber, you’ll see the following output:
C:...> cucumber Hello, world! 0 scenarios 0 steps 0m0.000s
But for now, let’s remove that greeting again and start by adding feature files!
As stated before, features live in their own file each, but are more akin to user stories in concept. It’s therefore likely that you will want to collect several feature files into bigger features such as that newsletter or online shop example.
Luckily, cucumber assists you in that. It will look for feature files directly in the
features folder, and execute them in alphanumerical file name order. For that reason, we recommend prefixing feature files with a conceptual feature name, followed by the name we gave the user story/cucumber feature. For now we’re not testing any real system, so we’ll use “test” as the name of the encompassing feature, arriving at a file name of
You’ll note we’re using the
.feature file name extension. That is what cucumber expects, but the file format is just plain text. Go on and copy & paste the example above into that file.
If you run cucumber now, the output won’t change. After all, we’ve not added any test scenarios yet for cucumber to execute. So let’s do that!
A First Scenario
Scenarios follow a similar pattern to features, in that they have a name and indented description. The scenario is added to the feature file it logically belongs to, and indented at the same level as the feature description. A feature can contain any number of scenarios, but of course they should test the same user story!
Feature: Example Feature In order to become productive As a test automation engineer/product manager I want to understand the basics of cucumber Scenario: My First Test Scenario Description Scenario: My Second Test Scenario Description
As Gherkin is structured English specifically designed for writing test scenarios, it makes sense that not any description for a scenario will do, but instead you’ll have to follow some simple rules:
Feature: Example Feature In order to become productive As a test automation engineer/product manager I want to understand the basics of cucumber Scenario: My First Test Scenario Given I have a variable with the numeric value "five" And I have a variable with the numeric value "three" When I add the two variables Then the result should be the numeric value "eight".
- Use Given to list preconditions.
- Use When to list all actions you take.
- Use Then to list your expectations and the outcome.
- Finally, use And whenever a precondition, action or expectation/outcome consists of multiple parts.
So let’s save that feature file and run cucumber again!
What you’ll notice is that cucumber outputs the feature file contents, with some additional color coding and comments added. This is a core concept of the cucumber testing tool, namely that test scenario definitions and test run output should be the same!
Each of the lines of test scenario description you provided represents a test step. Immediately following the feature file contents, cucumber outputs the following:
1 scenario (1 undefined) 4 steps (4 undefined) 0m0.032s
It tells you that cucumber correctly found and interpreted our first example scenario, and identified four test steps. It also complains that none of the four test steps were defined, which means it counts the entire scenario as undefined.
This is where cucumber attempts to hook up the human readable test scenario descriptions to executable Ruby code. What it’s really complaining about is that it could not find Ruby code to execute for any of the test steps it identified. This Ruby code is called a step definition in cucumber lingo.
Following this summary, cucumber suggests how you can remedy the situation:
Given(/^I have a variable with the numeric value "([^"]*)"$/) do |arg1| pending # Write code here that turns the phrase above into concrete actions end When(/^I add the two variables$/) do pending # Write code here that turns the phrase above into concrete actions end Then(/^the result should be the numeric value "([^"]*)"\.$/) do |arg1| pending # Write code here that turns the phrase above into concrete actions end
We’ll do what cucumber suggests next.
Remember that folder named
features/step_definitions that cucumber created? That’s where step definitions live. Cucumber will load any Ruby file (ending in
.rb) in that folder, and try to match the gherkin step with some Ruby code. Let’s try this out by copying & pasting the above three snippets into a file
Now run cucumber again. You’ll see the output will have changed:
- Following the first step cucumber prints, it will print that this step is “TODO (Cucumber::Pending)”, with some details to follow. This is due to the
pendingline we copied in each step definition.
- The subsequent three lines are skipped.
- The summary now lists one pending and three skipped steps.
All in all, it means cucumber has found the code we copied into step definitions, but since the first one did not actually contain any useful test code, the remainder was not executed.
Looking at these Ruby snippets, you’ll probably notice a few other things:
- The first word of each step definition matches the first word of each step as it appears in the feature file. Cucumber defines the Ruby functions
Thento match different kinds of steps.
- What follows this first word in parantheses looks suspiciously similar to the remainder of the gherkin step. It is in fact a Regular Expression, a kind of language specialized in text pattern matching. Cucumber matches each line in a test scenario against all the regular expressions in the step definitions files to find the right piece of code to execute. We’ll learn more about regular expressions in a later post.
In order to implement meaningful step definitions, you’ll need to learn some Ruby programming and perhaps some regular expressions. Those will be topics of later posts. For now, let’s replace the step definitions we had in
steps.rb with the following code. It uses Ruby arithmetic, but only minimal other Ruby programming knowledge, so should be understandable to everyone.
a = 0 Given(/^I have a variable with the numeric value "five"$/) do a = 5 end b = 0 Given(/^I have a variable with the numeric value "three"$/) do b = 3 end d = 0 When(/^I add the two variables$/) do d = a + b end Then(/^the result should be the numeric value "eight"\.$/) do if not d == 8 raise "Invalid result" end end
bare initialized to zero. Within the matching step definition, they are assigned
dis initialized to zero. Within the matching step definition, it is set to the sum of
- In the last step, if
dis not equal to eight, an error is raised.
Run cucumber again. All steps, and therefore the entire scenario should now be marked as “passed”. And there you have it, a very simple test suite, that tests whether Ruby can correctly add two numbers.
In the next few blog posts, we’ll explore the following topics in some more depth:
- We will provide an introduction to Ruby, with just enough detail to write simple step definitions.
- There will be an overview of regular expression syntax. Aside from helping understand how cucumber matches gherkin steps to Ruby step definitions, it’s useful for testing real web applications later on.
- We’ll also explore more advanced features of cucumber itself.