Saturday, March 15, 2008

Behavior Driven Development in Rails - Easy and Fun!

What?

Behavior driven coding for Ruby, and especially, Ruby on Rails.

This is a summary and run-through of the excellent RailsEnvy Love Tests presentation.

Why?

  • Tests increase your confidence, and let you sleep at night.
  • Writing tests before writing you code makes you write better code.
  • Writing the tests and specifications first, you will know when you're done - all the tests pass!
  • Tests make for excellent documentation, in the sense that Examples are more powerful than Descriptions

Also - it's fun! Do it Red -> Green -> Refactor style with RSpec and Auto testing Zen.

How?

Let's consider the example of a user who has forgotten her email.

Writing good behavior specifications is like telling a short story - a good story defines your app behavior:

A user who has forgotten a password
- Should be able to enter and submit their email address
- Should be sent an email if their email address is valid
- Should be shown a notice if the email address is invalid
- Should be redirected to the login screen if their email is valid
- Should be shown an error message if the email address is not in the system, and be shown the form again.

We use RSpec to turn our story into action.

Install RSpec

Without rails (just the gem)

gem install rspec

For rails, with its beautiful helpers

ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec
ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec_on_rails

./script/generate rspec

To run your tests, you just do

rake rspec

OR you turn testing into an automated game. ZenTest will run tests *every time your files change* and pop up notifications for passes/fails using Growl/Snarl.

gem install ZenTest redgreen
autotest

Coding the behavior

Now actually write the RSpec code - or just flow by turning your English into RSpec with the awesome textmate YAML to RSpec plugin. Direction for easy install.

On to the testing. It is important to keep the component we're testing separated from the rest of the system. E.g. if we want to test the controller of a user retrieving a lost password, we don't want it to be dependent on the Model part working correctly at test times.

Enter the stage: Stubs and Mocks.

You use these to manage "canned" answers. E.g. if you want to test for a user with the name Greg who is in the system and entered his email correctly, you do


@user = mock_model(User).stub!(:first_name).returns("Greg") # Let there be a Greg
@user = mock_model(User, :first_name => "Greg") # Briefer syntax
User.stub!(:find_by_email).returns(@user) # Always find Greg

What to test?

Every line of code you write. Seriously. In Rails you write so little code that this should be possible. However, make sure that you don't slip into testing Rails - it is already rather well tested!

Examples

Validation


validates_presence_of :first_name
# To test it =>
@user.should_have_at_least(1).errors_on(:first_name)


Object relation mapping


has_many :user_roles
# Test
@user.user_roles.create(:role => "Admin")
@user.user_roles.count.should == 1


UI Redirection


redirect_to :action => 'index'
# Test
response.should redirect_to(:action => "index")


Do it

Read the RSpec website - it reads like a good book
Come up with and start a New project
Keep 100% Test coverage, or at least attempt it!
Feel good about yourself
Make sure tests pass

No comments: