Fasten your TDD red-green-refactor cycles
Posted: July 26, 2011 Filed under: Uncategorized Leave a comment »Having recently been bit by the TDD bug, I’ve been researching a lot about best practices when it comes to testing and speeding up the red-green-refactor process and I recently came across a really neat way to fasten the cycles further.
Guard - is a command line tool that allows you to handle events on file modifications. What that means is you can essentially configure it to monitor for modifications to specific files on your file system and perform corresponding actions. It is a great utility and can be used for carrying out a wide variety of actions.
There is a big list of ‘Guards’ available, which are essentially extensions for guard to guard specific types of files, you can find the list here in the Guard Wiki
Installation :
I would recommend you to use bundler to maintain your gem dependies, if you are doing so, simply add the following in your Gemfile
gem 'guard' gem 'guard-rspec' gem 'guard-cucumber' gem 'rb-inotify' gem 'libnotify' # This is optional for notifications on linux, for other OS' please look at https://github.com/guard/guard#readme for details for other OS'
Or if you’re not using bundler, simply install the above mentioned gems
manually. After having done that you can perform the following steps to setup
guard and configure it for both rspec & cucumber
$ guard init # This creates an empty Guardfile in the project's root, which it uses for configurations.
$ guard init rspec # This adds rspec guard to the Guardfile
$ guard init cucumber # This adds cucumber guard to the Guardfile
If you now look at your Guardfile, you will see something like this :
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard 'cucumber' do
watch(%r{^features/.+\.feature$})
watch(%r{^features/support/.+$}) { 'features' }
watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
end
guard 'rspec', :version => 2 do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
# Rails example
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch('spec/spec_helper.rb') { "spec" }
watch('config/routes.rb') { "spec/routing" }
watch('app/controllers/application_controller.rb') { "spec/controllers" } # Capybara request specs
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
end
The Guardfile is quite self-explanatory, but you can have a look at
https://github.com/guard/guard#readme for more details.
After having done that, all you need to do is start guard by issuing :
guard [start] # start is optional
Now whenever you create or modify either your spec or cucumber feature files, guard will automatically run the tests for your for the individual file that has been modified / added. This is really cool since all you need to do is write your tests and save the files and it will automatically run your tests.
If you have installed the library for notifications (libnotify in linux), you will see a cool notification of the tests instantly, indicating with the help of an icon whether the tests passed or failed. This is indeed a faster approach to following the red-green-refactor TDD cycles.
More with cucumber.
Posted: July 25, 2011 Filed under: Uncategorized Leave a comment »Sometime back we wrote about getting started with cucumber. Hope you liked it. In this post I want to share you some tips which will make your cases more elegant and managable.
DRYing your test cases.
When you start using cucumber exhaustively, you will notice at one point that you end up writing the same steps again and again. And gradually when your feature file grows bigger and bigger it becomes very tough manage.
Feature: Signin
As a registered user
I want to signin with my details
So that I can access my dashboard
Scenario: Successful sign in
Given I am a registered user and my email is "deepak@example.com" and my password is "password"
When I am on the "signin" page
And I fill in "Email" with "deepak@example.com"
And I fill in "Password" with "password"
And I press "Sign In"
Then I should be on the "dashboard" page
And I should see "Signed in successfully."
Scenario: Checking unread messages
Given I am a registered user and my email is "deepak@example.com" and my password is "password"
When I am on the "signin" page
And I fill in "Email" with "deepak@example.com"
And I fill in "Password" with "password"
And I press "Sign In"
Then I should be on the "dashboard" page
And I should see "Signed in successfully."
When I have "2" unread messages
Then I should see "you have 2 unread messages."
Scenario: Checking the recent activites
Given I am a registered user and my email is "deepak@example.com" and my password is "password"
When I am on the "signin" page
And I fill in "Email" with "deepak@example.com"
And I fill in "Password" with "password"
And I press "Sign In"
Then I should be on the "dashboard" page
And I should see "Signed in successfully."
When I have some recent activities
Then I should see those recent activities in the dashboard
If you look at the above example, in every scenario, 7 steps are repeated again and again. The ideal solution is to DRY them up. Cucumber allows you accomplish this by setting up a background.
Feature: Signin
As a registered user
I want to signin with my details
So that I can access my dashboard
Background:
Given I am a registered user and my email is "deepak@example.com" and my password is "password"
When I am on the "signin" page
And I fill in "Email" with "deepak@example.com"
And I fill in "Password" with "password"
And I press "Sign In"
Then I should be on the "dashboard" page
And I should see "Signed in successfully."
Scenario: Checking unread messages
When I have "2" unread messages
Then I should see "you have 2 unread messages."
Scenario: Checking the recent activites
When I have some recent activities
Then I should see those recent activities in the dashboard
This way of writing the repetitive steps in the background is best a practice to follow. It is worth knowing that the background is executed before the every scenario is executed. Which means the order of execution will be,
Background -> Scenario1,
Background -> Scenario2,
Background -> Scenario3.
Sadly you can only set up a single background for every feature. It would be really interesting if cucumber had allowed backgrounds with a context. Which means we could have some thing like,
Background1 -> Scenario1
Background1 -> Scenario3
Background1 -> Scenario5Background2 -> Scenario2
Background3 -> Scenario4
The only way we can accomplish the above is to break them into multiple features.
#feature1
Background1 -> Scenario1
Background1 -> Scenario3
Background1 -> Scenario5#feature2
Background2 -> Scenario2
Background3 -> Scenario4
DRYing up further more
If you are lazy enough to break it down into multiple features, then there is some good news for you. Yes, cucumber allows you to group a set of steps into a single step, and this can be done in your step definition.
Scenario: Successful sign in
Given 'I "deepak@example.com" have an invitation for the role "Admin"'
Then 'I should receive an email'
When 'I open the email'
Then 'I should see "Invite to join example.com" in the email subject'
And 'I should see "Click here to sign up" in the email text body part'
You can group all of the above steps into one.
#In your step definition
Given /^I have opened my invitation email and clicked the sign up link$/ do |arg1|
Given 'I "deepak@example.com" have an invitation for the role "Admin"'
Then 'I should receive an email'
When 'I open the email'
Then 'I should see "Invite to join example.com" in the email subject'
And 'I should see "Click here to sign up" in the email text body part'
end
Once you have done the above, you can use it directly in your feature.
Scenario: Successful sign in
Given I have opened my invitation email and clicked the sign up link