Steps and Step Definitions

Let’s start by clarifying the distinction between a step and a step definition.

Each Gherkin scenario is made up of a series of steps, written in plain language. On its own, a step is just documentation; it needs a step definition to bring it to life. A step definition is a piece of Ruby code that says to Cucumber, “If you see a step that looks like this…, then here’s what I want you to do….”

When Cucumber tries to execute each step, it looks for a matching step definition to execute. So, how does Cucumber match a step definition to a step?

Matching a Step

Gherkin steps are expressed in plain text. Cucumber scans the text of each step for patterns that it recognizes, which you define using a regular expression. If you haven’t used regular expressions before, then just think of them like a slightly more sophisticated version of the wildcards you’d use to search for a file. Although they can look intimidating at first, there are actually only a small number of patterns you need to get a great deal of mileage out of them. All of those patterns will be covered in this chapter; if you’re already well familiar with regular expressions, you might want to skim over the next few sections, up until Returning Results.

Let’s take the ATM example from the previous chapter:

step_definitions/intro/features/cash_withdrawal.feature
 
Feature:​ Cash withdrawal​
 
Scenario:​ Successful withdrawal from an account in credit​
 
Given ​I have $100 in my account​
 
When ​I request $20​
 
Then ​$20 should be dispensed

As Cucumber executes this feature, it will come to the first step of the scenario, Given I have $100 in my Account and say to itself, Now, do I have any step definitions that match the phrase I have $100 in my Account?

A simple regular expression that will match this step would look like this:

 
/I have $100 in my Account/

In Ruby, regular expressions are denoted by those forward slashes. Notice that we’ve had to escape the dollar sign with a backslash. That’s because the dollar sign can have a special meaning in a regular expression, but in this case we want to interpret it literally. We’ll come back to these special characters a bit later in the chapter.

Aslak says:
Aslak says:
Avoiding Regular Expressions

Cucumber allows you to define step definitions using strings instead of regular expressions. This might seem simpler at first, but it has other problems, which I’ll illustrate with an example. Here is a step definition that uses a plain string:

 
Given ​"I have 100 in my Account"​ ​do
 
end

We couldn’t write $100 here, because the $ has a special meaning when you define step definitions with strings. Any $ signs—including any letter or number following it—will be interpreted as an argument, which you will learn more about in Capturing Arguments. This step definition uses an argument:

 
Given ​"I have $amount in my Account"​ ​do​ |amount|
 
end

This step definition would match both of the following Gherkin steps:

 
Given ​I have 100 in my Account​
 
Given ​I have $100 in my Account

In the first case, the step definition’s amount argument would have the value "100". In the second case, it would have the value "$100". If our step definition expects a string with only digits, this can be problematic. We have no way to enforce a consistent way to write Gherkin steps, and the step definitions have to anticipate many kinds of input.

This is why using strings instead of regular expressions is not as advantageous as you might think. They give far less control over what gets matched and what arguments a step definition can receive.

If Cucumber sees a step definition with this regular expression, it will execute it when it comes to the first step of our scenario. So, how do we create a step definition?

Creating a Step Definition

Step definitions live in regular Ruby files in the features/step_definitions folder. To create a step definition, you can use the special Cucumber method Given, like this:

 
Given /I have $100 in my Account/ ​do
 
# TODO: code that puts $100 into User's Account goes here
 
end

You’ll typically put several step definitions like this together in the same Ruby file. Cucumber will load all the files in the features/step_definitions directory, so it’s really up to you how exactly you want to organize them. We suggest keeping a separate file per domain entity so that step definitions that work with similar parts of the system are kept together.

Let’s examine the step definition in detail. This is a Ruby file, and we’re calling the special Cucumber method Given, which tells Cucumber that we want to register a step definition. We pass the Given method two things: a regular expression to match one or more steps (the bit between the forward slashes) and a block of Ruby code (the bit between the do...end) to execute if it does. Cucumber stores the code block for later to execute if it comes across a matching step.

You can also use the methods When or Then to create a step definition in just the same way.

Given, When, Then Are the Same

It doesn’t actually matter which of the three methods you use to register a step definition, because Cucumber ignores the keyword when matching a step. Under the hood, all of the keyword methods are aliases for Cucumber::RbDsl#register_rb_step_definition. The keywords are really just there for extra documentation to help you express the intent of each step or step definition.

This means that, whether it was created with the method Given, When, or Then, a step definition will match any Gherkin step as long as the regular expression matches the main text of the step. Figure 4, Cucumber ignores keywords when matching a step highlights what Cucumber sees when it scans a scenario for matching step definitions.

images/step-defs-text-highlight.png

Figure 4. Cucumber ignores keywords when matching a step.

This flexibility can be really handy, as we’ll show you later, but there is one gotcha to watch out for. Let’s take a look at an example.

Imagine you have already implemented your ATM withdrawal scenario, including writing a step definition for Given I have $100 in my Account. So, you have a step definition that matches the text I have $100 in my Account and creates an account with $100 in it. A few weeks later that scenario is a dim and distant memory, and you get a new requirement to give all new accounts a $1 gift. You sit down with your domain expert and write the following scenario:

 
Scenario:​ New accounts get a $1 gift​
 
Given ​I have a brand new Account​
 
And ​I deposit $99​
 
Then ​I have $100 in my Account

That looks reasonable, doesn’t it? We set up the new account, deposit some money, and then check that the new balance is what we’d expect it to be. But can you see what’s going to happen if we run this new scenario together with our original ATM withdrawal scenario?

Let’s look at our original step definition again:

 
Given /I have $100 in my Account/ ​do
 
# TODO: code that puts $100 into User's Account goes here
 
end

Now that we’ve learned that Cucumber ignores the Given/When/Then keyword when matching a step, we can see that this original step definition is also going to match the last step of our new scenario, Then I have $100 in my Account. Surprise! We expected that step to check the balance of the account, but instead it’s going to put $100 into the account!

You obviously need to be careful in this situation, because we could easily have had a scenario that was giving us a false positive: passing when it should have been failing. It might not seem like it, but Cucumber’s flexibility has actually helped us here, by exposing some quite subtle ambiguity in the language used in each of the steps. The best way we’ve found to avoid this kind of problem is to pay careful attention to the precise wording in your steps. You could change both steps to be less ambiguous:

 
Given ​I have deposited $100 in my Account​
 
Then ​the balance of my Account should be $100

By rewording the steps like this, you’ve made them better at communicating exactly what they will do when executed. Learning to spot and remove this kind of ambiguity is something that takes practice. Paying attention to the distinction in wording between two steps like this can also give you hints about concepts that may not be expressed in your code but need to be. It might seem pedantic, but we’ve found that teams who pay this much careful attention to detail write much better software, faster.

Speaking in Tongues

If you’re using a different spoken language than English in your features, you can still use the same language when registering step definitions. Cucumber creates an alias of Cucumber::RbDsl#register_rb_step_definition for every spoken language, so a team working in Spain, for example, could use the following:

 
Dado /tengo $100 en mi Cuenta/ ​do
 
# TODO: code that puts $100 into User's Account goes here
 
end

Remember, you can discover the keywords for your spoken language using the command cucumber --i18n <language>.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.202.209