Wednesday, November 12, 2008

Playing with Merb+Cucumber+Webrat (1)

In my previous post, we have setup the environment for playing with merb+cucumber+webrat. Now lets start playing !! Under ur merb project directory, place the following code in features/users.feature:
Feature: Manage Users
To have the system support multiple users
I should be able to manage users in the system

Scenario: View Existing Users
Given the following 5 users exist in the system:
| name | email |
| Peter | peter@abc.com |
| John | john@xyz.org |
| Jane | jane@efg.net |
| Mary | mary@hij.info |
| Cathy | cathy@stu.us |
When I go to /users
Then I should see all 5 users
And all users should be ordered by name
And I should see Peter having "Email" as "peter@abc.com"
Yup, pretty basic for any system with basic user support. To run the above feature, you can use one of the followings:

#$ rake feature[/users]
#$ rake features

The difference is that the 1st command runs only the specified feature, while the later runs all. Usually when u are focusing on a feature, you will run the 1st command. In ur console, you will get the following output (i'm leaving out the colors to keep my life simple):

Feature: Manage Users  # features/users.feature
To have the system support multiple users
I should be able to manage users in the system
Scenario: View Existing Users # features/users.feature:5
Given the following 5 users exist in the system: # features/users.feature:6
When I go to /users # features/steps/common_webrat.rb:4
Then I should see all 5 users # features/users.feature:14
And all users should be ordered by name # features/users.feature:15
And I should see Peter having email "peter@abc.com" # features/users.feature:16

1 steps skipped
4 steps pending

You can use these snippets to implement pending steps:

Given /^the following 5 users exist in the system:$/ do
end

Then /^I should see all 5 users$/ do
end

Then /^all users should be ordered by name$/ do
end

Then /^I should see Peter having email "peter@abc.com"$/ do
end

Finished in 0.006268 seconds

0 examples, 0 failures

From your console, you will probably notice some very nice and intuitive colorings, and since they are very intuitive, i shall skip over them. Notice that for pending (yellow) steps, cucumber tries to be helpful by suggesting how you are going to write them. Now, let's take cucumber's hint, copy-and-paste those suggested lines, and open features/steps/users_steps.rb:
Given /^the following 5 users exist in the system:$/ do
end
Then /^I should see all 5 users$/ do
end
Then /^all users should be ordered by name$/ do
end
And do:

#$ rake feature[/users]

Feature: Manage Users  # features/users.feature
To have the system support multiple users
I should be able to manage users in the system
Scenario: View Existing Users # features/users.feature:5
Given the following 5 users exist in the system: # features/steps/users_steps.rb:1
expected 0 block argument(s), got 1 (Cucumber::ArityMismatchError)
features/steps/users_steps.rb:1:in `/^the following 5 users exist in the system:$/'
features/users.feature:6:in `Given the following 5 users exist in the system:'
When I go to /users # features/steps/common_webrat.rb:4
Then I should see all 5 users # features/steps/users_steps.rb:4
And all users should be ordered by name # features/steps/users_steps.rb:7

1 steps failed
4 steps skipped

OH NO !! What have we done wrong ?? No worries, we are on the right track :]. The red failure lines are intended. Let's fix the problem by editing features/steps/users_steps.rb:
Given /^the following 5 users exist in the system:$/ do |attrs_table|
end
Then /^I should see all 5 users$/ do
end
Then /^all users should be ordered by name$/ do
end
Then /^I should see Peter having "Email" as "peter@abc.com"$/ do
end
And do:

#$ rake feature[/users]

Feature: Manage Users  # features/users.feature
To have the system support multiple users
I should be able to manage users in the system
Scenario: View Existing Users # features/users.feature:5
Given the following 5 users exist in the system: # features/steps/users_steps.rb:1
When I go to /users # features/steps/common_webrat.rb:4
Then I should see all 5 users # features/steps/users_steps.rb:4
And all users should be ordered by name # features/steps/users_steps.rb:7
And I should see Peter having "Email" as "peter@abc.com" # features/steps/users_steps.rb:10

5 steps passed

OH NO !! What have we done wrong ?? Just adding attrs_table and we get a 'passed' for all steps ?? We've not written any model/view/controller code yet, so how can ... One key point to note here is that as long as we have a matching step, and the step doesn't trigger a failure, it is considered a 'passed'. Anyway, there is a workaround for this. Since cucumber does not yet support any pending/todo in a step, we can rewrite features/steps/users_steps.rb:
Given /^the following 5 users exist in the system:$/ do |attrs_table|
pending
end
Then /^I should see all 5 users$/ do
pending
end
Then /^all users should be ordered by name$/ do
pending
end
Then /^I should see Peter having "Email" as "peter@abc.com"$/ do
pending
end
And do:

#$ rake feature[/users]

Feature: Manage Users  # features/users.feature
To have the system support multiple users
I should be able to manage users in the system
Scenario: View Existing Users # features/users.feature:5
Given the following 5 users exist in the system: # features/steps/users_steps.rb:1
undefined local variable or method `pending' for # (NameError)
./features/steps/users_steps.rb:2:in `Given /^the following 5 users exist in the system:$/'
features/users.feature:6:in `Given the following 5 users exist in the system:'
When I go to /users # features/steps/common_webrat.rb:4
Then I should see all 5 users # features/steps/users_steps.rb:4
And all users should be ordered by name # features/steps/users_steps.rb:7
And I should see Peter having "Email" as "peter@abc.com" # features/steps/users_steps.rb:10

1 steps failed
4 steps skipped

Pretty ugly, are we back to square one? Not really, as long as we are well aware of the actual reason. Let's take a break take a look at how Aslak (cucumber's author) sums up the style of development with BDD and cucumber:
  1. when u have a new feature to implement, start by writing a new feature (a Feature in features/*.feature file) or scenario (a Scenario under an existing features/*.feature)
  2. describe the feature/scenario by filling in Then, followed by When, and finally Given
  3. run the features (make sure the new steps are yellow or red)
  4. write code to address the yellow/red steps
  5. repeat 3-4 to get all green steps
  6. when it comes to the nitty gritty details of your classes, use rspec to write specs before writing code
I shall not go into the iterating of writing codes and running the features since this will well go into many many pages, but before i end this post, let's take a look at how we can rewrite features/steps/users_steps.rb to make it more generic, less coupled to users:
Given /^the following (\d+) (\w+) exist in the system:$/ do | count, resource, attrs_table|
pending
end
Then /^I should see all (\d+) (\w+)$/ do | count, resources |
pending
end
Then /^all (\w+) should be ordered by (\w+)$/ do | resources, attribute |
pending
end
Then /^all (\w+) should be ordered by (\w+)$/ do | resources, attribute |
pending
end
Then /^I should see (\w+) having "(.*)" as "(.*)"$/ do | resource_nick, attribute, value |
pending
end
With the above, the steps are more generic and we can probably placed them into features/steps/common_result_steps.rb, and remove features/steps/users_steps.rb. Should we call it refactoring of our steps ?

The sample codes for this exercise (tag 0.1.x) can be downloaded from:
http://github.com/ngty/ty_cucumber_salad

No comments: