This is short course in implementing your first cucumber test.
We use a control test and build from there =)
Add cucumber to the Gemfile. Bundler is a ruby gem manager which we'll use to install the cucumber gem and lock the version.
The features/support/env.rb
file is a ruby file that gets loaded and execute whenever the cucumber environment starts up.
Install the bundle:
$: bundle install
Using builder (3.2.2)
Using diff-lcs (1.2.5)
Using multi_json (1.9.2)
Using gherkin (2.12.2)
Using multi_test (0.1.1)
Using cucumber (1.3.14)
Using bundler (1.5.2)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
Verify cucumber runs correctly:
$ bundle exec cucumber
env.rb has loaded! O_o
0 scenarios
0 steps
0m0.000s
Note the message we put in env.rb
is correctly being executed and displayed.
The feature we need to build is for a user to mention another user in a comment and have the system notify users anytime they are mentioned in a comment.
Cucumber encourages us to write tests in natural language so here we'll mock up a high level explanation of our feature as well as a simple test to prove our feature works.
We really want to focus in on this feature so rather than concentrate on how users create comments, let's put the comment in a vacuum and assert that a comment is capable of parsing and processing @mentions.
Verify the mentions feature is run:
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/mentions.feature:7
Then the comment's mentions should include the user "jade" # features/mentions.feature:8
1 scenario (1 undefined)
2 steps (2 undefined)
0m0.012s
You can implement step definitions for undefined steps with these snippets:
Given(/^I write a comment with body "(.*?)"$/) do |arg1|
pending # express the regexp above with the code you wish you had
end
Then(/^the comment's mentions should include the user "(.*?)"$/) do |arg1|
pending # express the regexp above with the code you wish you had
end
The output tells us we have 2 steps that are undefined and gives us snippets to help us define our steps.
The cucumber terminal output autogenerated some step definitions that match our @mentions feature in the previous step so we'll use those.
Verify the new steps are recognized:
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
TODO (Cucumber::Pending)
./features/step_defs.rb:2:in `/^I write a comment with body "(.*?)"$/'
features/mentions.feature:7:in `Given I write a comment with body "Hello @jade"'
Then the comment's mentions should include the user "jade" # features/step_defs.rb:5
1 scenario (1 pending)
2 steps (1 skipped, 1 pending)
0m0.008s
The output now tells us we have 1 skipped step and 1 pending step.
A pending step does us no good because it is not testing anything. The steps are meant to encapsulate the code logic needed to carry out the given feature.
Since we have no code logic, we'll start by building out a Comment
class that will support parsing and processing @mentions.
As per the language of the first step definition we need a way to make a comment with the incoming text assigned to its body. Note the very logical comment.body
method now seems to correctly hold the given text.
Verify the code we just made:
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
uninitialized constant Comment (NameError)
./features/step_defs.rb:2:in `/^I write a comment with body "(.*?)"$/'
features/mentions.feature:7:in `Given I write a comment with body "Hello @jade"'
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
Failing Scenarios:
cucumber features/mentions.feature:6 # Scenario: Comment has single @mention
1 scenario (1 failed)
2 steps (1 failed, 1 skipped)
0m0.007s
It seems Comment
is not initialized; it means Cucumber is not aware of the Comment
class we just made.
Remember the env.rb
file is loaded when cucumber initializes its environment. We'll add the base directory to the load path which will allow us to load our comment class file into the cucumber environment.
$: bundle exec cucumber
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
Hello @jade
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
TODO (Cucumber::Pending)
./features/step_defs.rb:7:in `/^the comment's mentions should include the user "(.*?)"$/'
features/mentions.feature:8:in `Then the comment's mentions should include the user "jade"'
1 scenario (1 pending)
2 steps (1 pending, 1 passed)
0m0.008s
The output tells us our first step is indeed now working (seems to be green) but our second step is still pending.
Let's try to implement the second step just like we did the first step. comment.mentions
seems like the most practical interface for returning the mentions on a comment.
Let's run the test again and see if our new code helped:
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
Hello @jade
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
undefined local variable or method `comment' for #<Object:0x007ffa249d33b8> (NameError)
./features/step_defs.rb:7:in `/^the comment's mentions should include the user "(.*?)"$/'
features/mentions.feature:8:in `Then the comment's mentions should include the user "jade"'
Failing Scenarios:
cucumber features/mentions.feature:6 # Scenario: Comment has single @mention
1 scenario (1 failed)
2 steps (1 failed, 1 passed)
0m0.012s
The output tells us comment
is not defined anywhere. =/
Cucumber is right. Since all step definitions are just ruby blocks, any local variables assigned in the block are only scoped to that particular code block.
We need a way to share state across these step definitions. Well cucumber is not so complicated; all the code running in the cucumber environment is running in the same environment, so you are safe to treat cucumber like any basic ruby environment.
We'll create instance variables which will persist across the environment for the duration of the cucumber environment instance.
The first step will run and set @comment
. Any steps after will now have access to @comment
.
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
Hello @jade
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
1 scenario (1 passed)
2 steps (2 passed)
0m0.009s
The output shows finally that our test has passed! But are we really testing our code?
The tests pass but there is no actual test in our code! We'll add rspec-expectations in order to assert expectations against our code logic.
- https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers
- https://github.com/rspec/rspec-expectations#readme
Update the bundle with rspec:
$: bundle install
Using builder (3.2.2)
Using diff-lcs (1.2.5)
Using multi_json (1.9.2)
Using gherkin (2.12.2)
Using multi_test (0.1.1)
Using cucumber (1.3.14)
Using rspec-core (2.14.7)
Using rspec-expectations (2.14.5)
Using rspec-mocks (2.14.6)
Using rspec (2.14.1)
Using bundler (1.5.2)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
Hello @jade
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
undefined method `include?' for nil:NilClass (NoMethodError)
./features/step_defs.rb:7:in `/^the comment's mentions should include the user "(.*?)"$/'
features/mentions.feature:8:in `Then the comment's mentions should include the user "jade"'
Failing Scenarios:
cucumber features/mentions.feature:6 # Scenario: Comment has single @mention
1 scenario (1 failed)
2 steps (1 failed, 1 passed)
0m0.005s
The output is telling us include?
is not defined on nil
. Looking at the code, the nil
in this case is what comment.mentions
is returning.
We'll implement comment#mentions
to split the body text on spaces giving us an array of words. Any word that starts with @
will be processed as a mention.
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
Hello @jade
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
expected ["@jade"] to include "jade" (RSpec::Expectations::ExpectationNotMetError)
./features/step_defs.rb:7:in `/^the comment's mentions should include the user "(.*?)"$/'
features/mentions.feature:8:in `Then the comment's mentions should include the user "jade"'
Failing Scenarios:
cucumber features/mentions.feature:6 # Scenario: Comment has single @mention
1 scenario (1 failed)
2 steps (1 failed, 1 passed)
0m0.004s
Here we see we almost got the correct name but we forgot to compensate for the actual @
signifier token.
The easiest thing to do is just drop @
from the mentioned word.
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
Hello @jade
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
1 scenario (1 passed)
2 steps (2 passed)
0m0.003s
So far so good, but comments should probably support multiple user @mentions right?
Let's write a new scenario where comments have multiple mentions.
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
Hello @jade
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
Scenario: Comment has multiple @mentions # features/mentions.feature:10
Given I write a comment with body "Hello @jade and @sam, how is it going?" # features/step_defs.rb:1
Hello @jade and @sam, how is it going?
Then the comment's mentions should include the users "jade" and "sam" # features/mentions.feature:12
2 scenarios (1 undefined, 1 passed)
4 steps (1 undefined, 3 passed)
0m0.005s
You can implement step definitions for undefined steps with these snippets:
Then(/^the comment's mentions should include the users "(.*?)" and "(.*?)"$/) do |arg1, arg2|
pending # express the regexp above with the code you wish you had
end
As before, we haven't actually implemented the step definition so we'll use cucumber's suggestion and add it.
We'll also implement the code logic to verify that comment.mentions
will correctly include multiple mentions.
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
Hello @jade
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
Scenario: Comment has multiple @mentions # features/mentions.feature:10
Given I write a comment with body "Hello @jade and @sam, how is it going?" # features/step_defs.rb:1
Hello @jade and @sam, how is it going?
Then the comment's mentions should include the users "jade" and "sam" # features/step_defs.rb:10
expected ["jade", "sam,"] to include "sam" (RSpec::Expectations::ExpectationNotMetError)
./features/step_defs.rb:12:in `/^the comment's mentions should include the users "(.*?)" and "(.*?)"$/'
features/mentions.feature:12:in `Then the comment's mentions should include the users "jade" and "sam"'
Failing Scenarios:
cucumber features/mentions.feature:10 # Scenario: Comment has multiple @mentions
2 scenarios (1 failed, 1 passed)
4 steps (1 failed, 3 passed)
0m0.007s
We are close but the test failed! It seems we forgot to account for characters that are not meant to be part of user names.
Applications should always sanitize usernames so we are going to restrict usernames to contain only word characters.
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
Hello @jade
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
Scenario: Comment has multiple @mentions # features/mentions.feature:10
Given I write a comment with body "Hello @jade and @sam, how is it going?" # features/step_defs.rb:1
Hello @jade and @sam, how is it going?
Then the comment's mentions should include the users "jade" and "sam" # features/step_defs.rb:10
2 scenarios (2 passed)
4 steps (4 passed)
0m0.005s
The tests pass!
Something is off about the step definitions. It seems we are getting a bit redundant. Let's try to refactor and clean up the code.
Note the ?:
regular expression syntax allows us to omit the match from getting captured. Otherwise our methods would have to take two arguments, the first just being a useless placeholder.
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
Hello @jade
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
Scenario: Comment has multiple @mentions # features/mentions.feature:10
Given I write a comment with body "Hello @jade and @sam, how is it going?" # features/step_defs.rb:1
Hello @jade and @sam, how is it going?
Then the comment's mentions should include the users "jade, sam" # features/step_defs.rb:6
2 scenarios (2 passed)
4 steps (4 passed)
0m0.005s
$: bundle exec cucumber
env.rb has loaded! O_o
Feature: User mentions
As a social butterfly
I want to mention my friends in comments I create
so they can be notified and interact with me.
Scenario: Comment has single @mention # features/mentions.feature:6
Given I write a comment with body "Hello @jade" # features/step_defs.rb:1
Hello @jade
Then the comment's mentions should include the user "jade" # features/step_defs.rb:6
Scenario: Comment has multiple @mentions # features/mentions.feature:10
Given I write a comment with body "Hello @jade and @sam, how is it going?" # features/step_defs.rb:1
Hello @jade and @sam, how is it going?
Then the comment's mentions should include the users "jade, sam" # features/step_defs.rb:6
Scenario: Comment has multiple @mentions of varying case # features/mentions.feature:14
Given I write a comment with body "Hello @Jade and @SAM, how is it going?" # features/step_defs.rb:1
Hello @Jade and @SAM, how is it going?
Then the comment's mentions should include the users "jade, sam" # features/step_defs.rb:6
expected ["Jade", "SAM"] to include "jade" (RSpec::Expectations::ExpectationNotMetError)
./features/step_defs.rb:10:in `block (2 levels) in <top (required)>'
./features/step_defs.rb:9:in `each'
./features/step_defs.rb:9:in `/^the comment's mentions should include the (?:user|users) "(.*?)"$/'
features/mentions.feature:16:in `Then the comment's mentions should include the users "jade, sam"'
Failing Scenarios:
cucumber features/mentions.feature:14 # Scenario: Comment has multiple @mentions of varying case
3 scenarios (1 failed, 2 passed)
6 steps (1 failed, 5 passed)
0m0.008s