Last Updated: September 29, 2021
·
12.88K
· robsonmwoc

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.