RSpec best practices

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. We’ve found the following practices helpful in writing elegant and maintainable specifications.

First #describe what you are doing …

Begin by using a #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 documenting 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

Write a #context for each execution path through a method; literally specifying the behavior of a method in a given context.

For example, the following method has 2 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

Create two contexts in the corresponding spec:

describe '#create' do
  context 'given valid credentials' do
  end

  context 'given invalid credentials' do
  end
end

Note the use of “given” in the argument to each #context. It 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.

#it only expects one thing

By striving to only having one expectation per example, you increase the readability of your specs.

A spec with multiple un-related expectations 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

Breaking out the expectations into separate examples clearly defines the behavior and results in easier to maintain examples:

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

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

Finally, don’t begin examples names with the word ‘should’.  It’s redundant and results in hard to read spec output. Likewise, don’t hesitate to use words like ‘the’ or ‘a’ or ‘an’ in your examples when they improve readability.

Prefer explicitness

#it, #its and #specify may cut down on the amount of typing but they sacrifice readability.  You now have to read the body of the example in order to determine what its specifying.  Use these sparingly if at all.

Let’s compare the documentation formatter output of the following:

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

With the explicit behavior descriptions below:

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

The first example results in blunt, code-like output with redundancy from using the word ‘should’ multiple times:

$ 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

The second results in a very clearly, readable specification:

$ 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

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.

Use the right matcher

RSpec comes with a lot of useful matchers to help your specs read more like language.  When you feel there is a cleaner way … there usually is!

Here are some of our favorites matchers, before and after they are applied:

# 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)

# before: straight comparison
collection.size.should == 4
# after: a higher level size expectation
collection.should have(4).items

Check out the docs and ask around.

Formatting

Use ‘do..end’ style multiline blocks for all blocks, even for one-line examples. Further improve readability with a single blank line between all blocks and at the beginning and end of the top level #describe.

Again compare:

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

With the clearly structured code below:

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.

Conclusion

As you can see, all these practices revolve around writing clear specifications readable by all developers. The ideal is to run all specs to not only pass but to have their output completely define your application. Every little step towards that goal helps, and we’re still learning better ways to get there. What are some of your best RSpec practices?

Update: This post is now available in Japanese thanks to Makoto Kuwata: http://jp.rubyist.net/magazine/?0032-TranslationArticle

About Jared Carroll

After a short stint in the fashion industry Jared found his true calling at Carbon Five. Yes... he looks like a serial killer in this photo. But really he is as gentle as a flower.
This entry was posted in Process, Web and tagged , . Bookmark the permalink.
  • http://ruby.zigzo.com/ Mario

    Great post!! I hope you don’t mind but i posted it to rubyflow.com

  • http://www.rimian.com.au Rimian

    Nice one. How refreshing to see clean code and practicality.

  • Pingback: Tweets that mention RSpec best practices « Carbon Five Community -- Topsy.com

  • Marcus

    Nice post. Good practices are always welcome.

  • http://tpo.pe/ Tim Pope

    If I’m not supposed to include “should”, then why do all the automatically generated descriptions include it?

    As in the output from:

    it do
    should redirect_to(sign_in_path)
    end

    I’m not necessarily disagreeing with you, but RSpec is. :)

    • Rudy Jahchan

      We were talking about the argument given to #it message, not #should itself. Clearly you should be using #should ;-)

      Of course, when the spec is run with documentation you would see a “should” appear in the output. We do feel that describing the example with a verb makes for less redundancy.

  • Pingback: Link dump for October 21st | The Queue Blog

  • ross

    What about shared contexts? Let’s say that my class has an attribute, which state modifies expected results of bunch of other methods. Should I put a separate block of describes for those methods, or should I add a ‘when the x is set to y’ to each global describe ‘#method’ block? The later one seems a little bit more redundant in my opinion.

  • http://www.danhawkins.me.uk Danny Hawkins

    You could not have written this at a better time, I’ve just committed to converting an old Rails 2.3 site to Rails 3 and am making sure the site has full set of stories and tests. There is so much around for RSpec but difficult to get a simple concise picture of where to start. Thanks.

  • http://prodis.pro.br Prodis a.k.a. Fernando Hamasaki de Amorim

    Great post. I will introduce the use of present tense verb rather than should in my team.

  • http://samullen.posterous.com Samuel

    Wow, high marks for thinking this through as much as you have and for the cleverness of the section titles. Tweeted and bookmarked.

  • Myron Marston

    “By striving to only having one expectation per example, you increase the readability of your specs.”

    This is generally good advice, but I’ve definitely seen it taken too far, and I’m sure I’ve been guilty of that. I also think that the benefit of one expectation per example isn’t primarily readability; it’s separating out the specifications of different behaviors into different examples so that the failure output tells your clearly which behavior is failing. Increased readability is a nice side effect, though.

    So, to rephrase what you said a bit: you should strive to specify one behavior per example, and if you have multiple expectations in the same example, it’s a good sign that you may be specifying multiple behaviors and you should consider separating them into separate examples.

    There are times when it’s absolutely correct to have multiple expectations in the same example. Here’s an example from RSpec’s own specs:

    http://github.com/rspec/rspec-core/blob/v2.0.1/spec/rspec/core/let_spec.rb#L19-22

  • Pingback: Twitted by felipedjinn

  • jared

    @ross – I duplicate the #context for each #describe. For example, say I have 2 actions in a controller that require being logged in, #new and #create. I’ll have 1 describe for each of those methods and the same 2 contexts (when logged in, when not logged in) nested within each #describe. I prefer to have 1 place where all the specs for a method are located. If I had 2 #contexts (when logged in, when not logged in) and then nested the #describe blocks in each I’d have 2 #describe’s per method. I think this approach is fine as well, either way you have duplication.

    @myron – Excellent point, I agree with you on saying “one behavior per example”. I’ve definitely had examples with more than 1 expectation.

    @all – Thanks for all the feedback!

  • http://blog.remygiard.com Rémy Giard

    I love that kind of post that suggest a nice way to work. I was looking for something like this about RSpec. Thanks for sharing!

  • http://rubyflare.com Michael MacDonald

    A great article! I particularly like how you aren’t telling everyone else that they are doing it wrong, just showing us what has worked for you. I didn’t know about using “expect” instead of “lambda” so I’ll definitely give that a try.

  • Pingback: RSpec best practices « Carbon Five Community « shiokers.com

  • http://blog.sidu.in Sidu Ponnappa

    If you’re doing a lot of response asserting, you should use these matchers for response codes: https://github.com/c42/rspec_matchers

    Gives you stuff like
    response.should be_unprocessable_entity
    response.should be_im_a_teapot

  • http://wilker-dev.com Wilker

    Nice article!

    I mean will be nice to have a similar article about best practices on Steak for acceptance specs, I know it can be almost the same, but for acceptance specs is common to check things on the way of execution (ending with a lot of assertions on same spec).

    Thanks a lot!

    • Rudy Jahchan

      Thanks Wilker!

  • http://www.techwench.com neo

    great post how clear cut code saving and testing

  • http://sikachu.com Prem Sichanugrist

    Thank you for this nice article. However, I’m curious about testing the validation. Do you just making another `describe ‘validations’` block, or do you just don’t test it altogether?

    • http://abernardes.github.com/ Andre Bernardes

      I’m curious about that too. And what about associations? Do you test them as well?

      • Jared Carroll

        I don’t directly unit test associations. Instead, associations are indirectly tested through other tests, e.g., integration tests that exercise the whole stack.

        As for validations see my comment above: http://blog.carbonfive.com/2010/10/21/rspec-best-practices/#comment-318474430

      • Gumaro Melendez

        I usually just do @user.should respond_to(:posts). Really all that test is doing is ensuring I’ve written the has_many :posts line in the User model. Anything more than that and I’d feel like I’m testing the framework or no long unit testing and as Jared said should be caught in an integration test

  • Jared Carroll

    @prem – I spec model validations using “describe ‘#valid?’” and if necessary nested contexts such as “on create” or “on an update”.

  • Pingback: Delicious Bookmarks for November 12th from 03:01 to 03:06 « Lâmôlabs

  • nicolasiensen

    Nice article brow!

    Don’t you think that…

    its(:body) { should match(/sign in/i) }

    … is readable enough?

    • http://blog.maxaller.name/ Max

      He’s referring to the console output readability, not the code readability.

  • Jared Carroll

    @ nicolasiensen – thanks for the feedback

    I just prefer visually parsing Strings as opposed to punctuation like parens, symbols, and curly braces. In the end its just a personal preference that I feel results in the most easily readable specs.

  • makoto kuwata

    This post is great. So I will translate it into Japanese.
    I’ll let you the url after translation is prepared.
    Thank you for nice post.

  • http://vitobotta.com Vito Botta

    This is one of the best posts I’ve read so far on the subject. Thanks!

    • Dave Doolin

      This is one of the best posts I’ve read on testing, ever. Actually, it’s one of the best posts I’ve read on programming practice ever.

  • Pingback: RSpec: Best Practices by Carbon Five | FiveAM

  • arnodmental

    Hi,
    Thank you for this excellent post, I have a question though.

    You don’t explicitly refer to the HTTP verb in your examples, how would you add it ? Or do you think this is enough to have it implicit (ok for usual methods, but if I want to add some others I will need to choose a verb..)

    • Jared Carroll

      arnodmental,

      I used to include the HTTP verb in my example names. Since I tend to write RESTful controllers if felt redundant to mention the HTTP verb because I was just following Rails’ conventions e.g. #new is a GET, #create is a POST, etc. As for non-RESTful actions (i.e. NOT one of the normal 6 that a map.resources will give you) I’d probably still exclude the HTTP verb so its consistent with the other specs for RESTful actions.

  • Anonymous

    Great!
    How about multiline expect blocks?
    Do you use { } or do..end?

    • Anonymous

      When it comes to multiline examples I follow the Ruby multiline block convention of using do...end. I never use 1 line blocks with { } because I prefer consistent block usage throughout a file.

  • http://www.ivanzotov.ru/ Иван Зотов

    Great! Thank you!

  • Anonymous

    About #it only expects one thing…
    Whats the impact on performance of integration tests? I mean, I’m repeating the same request over and over again.
    It would be interesting to run a benchmark on that. Don’t you think?

    • Jared Carroll

      Good point pcasaretto. You will definitely slow your tests down by writing an example for each expected behavior. In my opinion, the tradeoff of speed for clearer test failures is worth it. However, I have refactored large, multiple example contexts down into a single example when it’s obviously slowing down the tests. I tend to not pre-optimize my tests and only worry about performance when it becomes an issue.

    • Schizunio

      You should check rspec-steps (https://github.com/LRDesign/rspec-steps) for integration testing – faster, still readable (or even sc readable) and just for the purpose

  • http://twitter.com/mcmire Elliot Winkler

    Nice article. What are your thoughts on using #let?

    • Jared Carroll

      I avoid #let and #let!. Every time I’ve tried to adopt #let into my specs I end up getting burned because #let is lazily initialized. Then I have to use #let!. I’d rather just setup all my instance variables in a #before which I know will always be executed before exercising the system under test. So I just use #before across the board and not a mix and match of #let and #let!.

      • http://smartic.us bryanl

        I think the lazy initialization is part of their usefulness. It also helps with readability:

        let(:order) { Factory(:order, custom_options} }

        is better than putting it in a before block.

      • http://grilix.net/ Gonzalo Arreche

        If it’s a precondition for your test, you wanna put it inside a before. But if it’s a collaborator, you could put it in a let, so you initialize it only when/if you need it.

  • Pingback: RSpec – Behavior-Driven Development for Ruby

  • http://www.facebook.com/tyler.dewitt Tyler DeWitt

    Thanks for the pointers. Quick question: How did you get “response.should redirect_to(user_path(assigns(:user)))” to work? When I try that, I get an error.

    • http://blog.carbonfive.com/ Jared Carroll

      Tyler,

      Could you provide a gist of the error?

    • http://grilix.net/ Gonzalo Arreche

      Just a comment, use assigns as a hash (assigns[:user]).

  • http://twitter.com/matiasleidemer Matias H. Leidemer

    Hey, great post! It’s old but it remais new :-)

    Just thinking about the “User.count.should == @count + 1″. I mean, should I check the database to see if the user got created? I’d rather use something like “User.should_receive(:create)”. IMHO it will make my tests run faster and isolated from the db layer.

    What do you think?

    • Jared Carroll

      Mocking the database will make your model specs faster. However, if you’re mocking the database, remember to have an integration test that actually does test the real objects.

      I typically don’t mock the database in my model specs. I’ve always found traditional state-based testing more straightforward and elegant than interaction-based testing with mocks. However, using state-based testing and an outside-in testing style will result in a test suite consisting of many “mini-integration tests”. But, by making no assumptions about implementation, your code will be easier to refactor without breaking tests.

      Either approach is fine. It often comes down to the team and how comfortable they are with both styles.

  • Pingback: Bessere Specs mit RSpec, ein Erfahrungsbericht | swimBlog

  • Pingback: Some important links | hemaruby