Rspec is pretty awesome, however due to its flexibility, often times I find that people write specs in ways that either a) aren't structured very well, or b) use the wrong terminology to group up common contexts & behaviors.

Update: Be sure to read David Chelimsky's suggestions in the comments.

A friend of mine who is fairly new to Rspec, and asked me to provide some feedback on some tests that he wrote.

Here is the before:

The only real problems here are:

  • Lots of duplicated setup code. If the initialization aspect of the Card design ever called for something other than a string, we'd have a lot of test code to fix.
  • Lots of "extra" code to test a simple value. If it smells like duplication to type "it 'has a value of 13'" and then type the same thing, only in ruby code, then you're right.

The rspec constructs I recommend to deal with this are `subject,` `let, and` `its` blocks.

  • Subject blocks allow you to control the initialization of the subject under test. If you don't have any custom initialization required, then you're given a default `subject` method already. All it does is call `new` on the class you're testing.
  • Let blocks allow you to provide some input to the subject block that change in various contexts. This way you can simply provide an alternative `let` block for a given value and not have to duplicate the setup code for the subject over again. Let blocks also work inside of `before :each` blocks if you need them.
  • Its blocks allow you to test methods on the subject that return a simple value. The benefit of using this over the more wordy version above is that it can actually format the test output for you.

Here is the same example above, using the above techniques to clean things up a bit.

And here is the output of the above spec:

Card
  #value
    Two of Hearts
      value
        should == 2
    Face Cards
      King of Clubs
        value
          should == 13
      Queen of Clubs
        value
          should == 12
      Jack of Hearts
        value
          should == 11
    Bad Value
      should raise StandardError

I think that's a big improvement.

Note: The code in this post is delivered via Github Gists, which unfortunately don't render in Google Reader. Click through to see the code.