Rspec Documentation



RSPEC
RSpec is a great tool in the behavior driven design process of writing human readable specifications that direct and validate the development of your application. I've found the following practices helpful in writing elegant and maintainable specifications.
First #describe What You Are Doing
Begin by using #describe for each of the methods you plan on defining, passing the method’s name as the argument. For class method specs prefix a "." to the name, and for instance level specs prefix a "#". This follows standard Ruby documentation practices and will read well when output by the spec runner.
describe User do    describe '.authenticate' do   end    describe '.admins' do   end    describe '#admin?' do   end    describe '#name' do   end  end
Then Establish The #context
Next use #context to explain the different scenarios in which the method could be executed. Each #context establishes the state of the world before executing the method. Write one for each execution path through a method.
For example, the following method has two execution paths:
class SessionsController < ApplicationController    def create     user = User.authenticate :email => params[:email],                              :password => params[:password]     if user.present?        session[:user_id] = user.id        redirect_to root_path     else        flash.now[:notice] = 'Invalid email and/or password'        render :new     end   end  end 
The spec for this method would consists of two contexts:
describe '#create' do   context 'given valid credentials' do   end    context 'given invalid credentials' do   end end 
Note the use of the word "given" in each #context. This communicates the context of receiving input. Another great word to use in a context for describing conditional driven behavior is "when".
describe '#destroy' do   context 'when logged in' do   end    context 'when not logged in' do   end end 
By following this style, you can then nest #contexts to clearly define further execution paths.
And Finally Specify The Behavior
Strive to have each example specify only one behavior. This will increase the readability of your specs and make failures more obvious and easier to debug.
The following is a spec with multiple un-related behaviors in a single example:
describe UsersController do    describe '#create' do    ...      it 'creates a new user' do     User.count.should == @count + 1     flash[:notice].should be     response.should redirect_to(user_path(assigns(:user)))     end   end  end 
Break out the expectations into separate examples for a more clear definition of the different behaviors.
describe UsersController do    describe '#create' do   ...      it 'creates a new user' do     User.count.should == @count + 1     end      it 'sets a flash message' do     flash[:notice].should be     end      it "redirects to the new user's profile" do     response.should redirect_to(user_path(assigns(:user)))     end   end  end 
Tips For Better Examples
Lose The Should
Don't begin example names with the word "should". It is redundant and results in hard to read spec output. Instead write examples by starting with a present tense verb that describes the behavior.
it 'creates a new user' do end  it 'sets a flash message' do end  it 'redirects to the home page' do end  it 'finds published posts' do end  it 'enqueues a job' do end  it 'raises an error' do end 
Don't hesitate to use words like "the" or "a" or "an" in your examples when they improve readability.
Use The Right Matcher
RSpec comes with a lot of useful matchers to help your specs read more like natural language. When you feel there is a cleaner way ... there usually is.
Here are some common matcher refactorings to help improve readability.
# before: double negative object.should_not be_nil # after: without the double negative object.should be # before: "lambda" is too low level lambda { model.save! }.should raise_error(ActiveRecord::RecordNotFound) # after: for a more natural expectation replace "lambda" and "should" with "expect" and "to" expect { model.save! }.to raise_error(ActiveRecord::RecordNotFound) # the negation is also available as "to_not" expect { model.save! }.to_not raise_error(ActiveRecord::RecordNotFound) # before: straight comparison collection.size.should == 4 # after: a higher level size expectation collection.should have(4).items 
Prefer Explicitness
#it, #its and #specify may cut down on the amount of typing but they sacrifice readability. Using these methods requires you to read the body of the example in order to determine what its specifying. Use these sparingly if at all.
Let's compare the output from the documentation formatter of the following spec that uses these more concise example methods.
describe PostsController do    describe '#new' do     context 'when not logged in' do       ...        subject do         response       end        it do         should redirect_to(sign_in_path)       end        its :body do         should match(/sign in/i)       end     end   end  end  $ rspec spec/controllers/posts_controller_spec.rb --format documentation  PostsController   #new     when not logged in       should redirect to "/sign_in"       should match /sign in/i 
Running this spec results in blunt, code-like output with redundancy from using the word "should" multiple times.
Here is the same spec using more verbose, explicit examples:
describe PostsController do    describe '#new' do     context 'when not logged in' do       ...        it 'redirects to the sign in page' do         response.should redirect_to(sign_in_path)       end        it 'displays a message to sign in' do         response.body.should match(/sign in/i)       end     end   end  end  $ rspec spec/controllers/posts_controller_spec.rb --format documentation  PostsController   #new     when not logged in       redirects to the sign in page       displays a message to sign in 
This version results in a very clear, readable specification.
Run Specs To Confirm Readability
Always run your specs with the "--format" option set to "documentation" (in RSpec 1.x the --format options are "nested" and "specdoc")
$ rspec spec/controllers/users_controller_spec.rb --format documentation  UsersController   #create     creates a new user     sets a flash message     redirects to the new user's profile   #show     finds the given user     displays its profile   #show.json     returns the given user as JSON   #destroy     deletes the given user     sets a flash message     redirects to the home page 
Continue to rename your examples until this output reads like clear conversation.
Formatting
Use "do..end" style multiline blocks for all blocks, even for one-line examples. Further improve readability and delineate behavior with a single blank line between all #describe blocks and at the beginning and end of the top level #describe.
Before:
describe PostsController do   describe '#new' do     context 'when not logged in' do       ...       subject { response }       it { should redirect_to(sign_in_path) }       its(:body) { should match(/sign in/i) }     end   end end 
And after:
describe PostsController do    describe '#new' do     context 'when not logged in' do       ...        it 'redirects to the sign in page' do         response.should redirect_to(sign_in_path)       end        it 'displays a message to sign in' do         response.body.should match(/sign in/i)       end     end   end  end 
A consistent formatting style is hard to achieve with a team of developers but the time saved from having to learn to visually parse each teammate's style makes it worthwhile.

Comments

Popular posts from this blog

Rails Memcache issues

Enabling password authentication for new ec2 box | ssh, ssh config, EC2 setup, new user in EC2, PasswordAuthentication

What's the difference between "include" and "require" in Ruby?