Cucumber World object
Hi,
recently I've working with Cucumber acceptance tests. At the beginning I was not so surprised, because I saw some lectures and presentations, and I was used to work with Capybara and Steak.
But the situation, is that my current project was started some time ago, and now I'm getting familiar with its acceptance tests.
The first thing I was concerned is related to the number of queries on each step. That means, almost every steps receive a parameter with the values to fetch from the database. For example:
Scenario: A registered user should be able to edit its profile
Given that I'm a registered user with email "my@email.com" and password "123123"
And I'm signed in with email "my@email.com" and password "123123"
When I access the user with email "my@email.com" profile page
And I click on "edit" link
Then I should see the profile edition form
This is a simple/hypothetical example, but could be very possible for a Cucumber newbie. The web steps for this was something like this:
Given(/^that I'm a registered user with email "(.*?)" and password "(.*?)"$/) do |email, pw|
User.create(email: email, password: password)
end
Given(/^I'm signed in with email "(.*?)" and password "(.*?)"$/) do |email, pw|
user = User.where("email = ? AND password = ?", email, pw)
visit sign_in_path
fill_in "user_email", :with => email
fill_in "user_password", :with => pw
click_button "Sign in"
end
When(/^I access the user with email "(.*?)" profile page$/) do |email|
user = User.find_by_email(email)
visit user_path(user)
end
And(/^I click on "(.*?)" link$/) do |link|
click_link link
end
And(/^I should see the profile edition form$/) do
page.should have_selector('#user-profile-edit')
end
The problem and the focus of this tip, are the lines with the User.where and User.findbyemail. These lines denotes that every time I need to do something with the user, subject of the scenario, I will fetch it from database.
I was struggling with a lot of steps using this kind of approach, and constantly felling bad, with performance issues on my Cucumber tests, and lack of clarity.
Googling a little I found this little article https://github.com/cucumber/cucumber/wiki/A-Whole-New-World that shows how to do helpers using the World object. The following was extracted from the article:
Any @instance_variable instantiated in a Step Definition will be assigned to the World, and can be accessed from other Step Definitions.
This means that you can use @instance_variables across all the Scenario avoiding constantly query to the database, information that could be on memory. Also, you can clean up the Cucumber steps, and write them in a clearer manner.
Scenario: A registered user should be able to edit its profile
Given that I'm a registered user
And I'm signed in
When I access my profile page
And I click on "edit" link
Then I should see the profile edition form
For me, this is so much clear and comprehensible then the other one. Now the steps looks like this:
Given(/^that I'm a registered user"$/) do
@user = User.create(email: "my@email.com", password: "123123")
end
And(/^I'm signed in"$/) do
visit sign_in_path
fill_in "user_email", :with => @user.email
fill_in "user_password", :with => @user.password
click_button "Sign in"
end
When(/^I access the user with email "(.*?)" profile page$/) do |email|
visit user_path(@user)
end
And(/^I click on "(.*?)" link$/) do |link|
click_link link
end
And(/^I should see the profile edition form$/) do
page.should have_selector('#user-profile-edit')
end
During the scenarios execution, a World instance is created, and each @instance_variable is shared across the Background and the used steps for that scenario. In the end of scenario's execution, these variables are trashed, a new scenario is started and so on.
For me, this was a great discovery, and I hope that could help you to write better Cucumber scenarios.
Don't be shammed to drop a comment.