Different ways of code reuse in RSpec

Hierarchical structure of RSpec, with nested context/describe blocks and hierarchical before/after hooks, topped with a bit of syntactic sugar like let/let! methods, already goes a long way towards clean and readable tests. However, more complex suites still can quickly go un-DRY and unwieldy without code reuse between example groups.

Fortunately, RSpec provides several different tools for reusing test code: helper methods and modules, custom matchers, shared examples and shared contexts. It also allows to create examples programmatically (eg. in a loop). In this post I’ll briefly introduce all these options and discuss use cases for each of them.

RSpec helper methods and modules

To encapsulate common, complex logic RSpec allows you to write arbitrary helper methods directly within an example group (or in a parent example group):

describe "parent context" do
  def parent_helper_method
    # ...
  end

  describe "nested context" do
    def nested_helper_method
      # ...
    end

    it "should see both inherited and local helper methods" do
      parent_helper_method
      nested_helper_method
    end
  end
end

To make helper methods even more reusable (between multiple, not nested example groups), it is possible to define them in a module:

module Helpers
  def helper_method
    # ...
  end
end

and then include it in your example:

describe "some context" do
  include Helpers

  it "should see helper methods from included module" do
    helper_method
  end
end

You can also mix-in helpers via extend instead of include or include them through RSpec configuration instead of directly in an example group, but I won’t dig into all the details right now.

When should you use RSpec helper methods and modules?

Two areas where helper methods are most useful are example initialization and execution.

The primary use of helper methods is to hide implementation details by grouping several low-level statements into more meaningful, higher level abstractions (with clear, expressive names). There is really no magic here, this is analogical to the function of methods in OO programming. E.g. if you want to test different outcomes of the last round in a perfect darts game, instead of manually go through the whole play like this:

describe "last round in a perfect darts game"
  before do
    2.times do
      3.times do
        player_1.shoot(20, triple)
      end
      3.times do
        player_2.shoot(20, triple)
      end
    end
  end
end

you can do it this way:

describe "last round in a perfect darts game"
  before do
    2.times do
      shoot_a_perfect_round(player_1)
      shoot_a_perfect_round(player_2)
    end
  end
end

or even:

describe "last round in a perfect darts game"
  before do
    shoot_a_perfect_two_rounds_for_both_players
  end
end

using helper methods to hide all the nitty-gritty details of what constitutes a perfect round in darts.

In a similar manner, you can also use helper methods as factories, hiding construction of complex objects (although this use case is largely superseded by dedicated libraries like factory_girl or machinist).

RSpec custom matchers

RSpec also allows you to easily create new matchers to encapsulate custom assertion logic into well named methods:

RSpec::Matchers.define :be_thrice_as_big_as do |expected|
  match do |actual|
    actual == 3 * expected
  end
end

describe "number nine" do
  it "should be three times three" do
    9.should be_thrice_as_big_as 3
  end
end

As in the case of RSpec helpers, this is only a basic example. RSpec custom matchers are way more powerful than this, providing chaining, custom failure messages, their own internal helper methods and so on. They can be also defined as modules instead of RSpec::Matchers.define method.

When should you use RSpec custom matchers?

Use cases for custom matchers are quite similar to these of helper methods (hiding low-level details and providing meaningful naming). They differ in their area of use – while helper methods are used for example initialization and execution, custom matchers are used for example verification.

Custom matchers also provide an option to define custom failure messages, what (especially in the case of more complex, multi-step assertions) is a great way to improve readability and expressiveness not only of the test code but also of the test output.

RSpec shared examples

Another powerful approach to code reuse offered by RSpec is to extract complete sections of system under test specifications into shared examples, like this:

shared_examples_for User do
  it "behaves in some way" do
    # ...
  end

  it "behaves in some other way" do
    # ...
  end
end

You can then use such extracted specification to define behavior of other classes:

describe Customer do
  it_behaves_like User

  it "also behaves in special, Customer-y way" do
    # ...
  end
end

describe Admin do
  it_behaves_like User

  it "also behaves in special, Admin-y way" do
    # ...
  end
end

Such inclusion of shared examples works exactly like they were hand-coded inside a host example:

describe Customer do
  context "behaves like User" do
    it "behaves in some way" do
      # ...
    end

    it "behaves in some other way" do
      # ...
    end
  end

  it "also behaves in special, Customer-y way" do
    # ...
  end
end

Once more, this is only a tip of an iceberg – RSpec shared examples offer a lot of additional flexibility, e.g. you may pass them extension blocks or parameters, they may use methods defined in their host and they can be included without nesting – but the above examples illustrate the core idea.

When should you use RSpec shared examples?

Shared examples are especially useful for specifying inheritance hierarchies (see the example above with User, Customer and Admin), mixins and “interfaces”:

shared_examples_for Serializable do
  # ...
end

describe Dictionary do
  it_behaves_like Serializable
  # ...
end

describe TreeGraph do
  it_behaves_like Serializable
  # ...
end

Shared examples can also be used when specifying different states of a single object, that have some behavior in common, e.g.:

shared_examples_for Account do
  # ...
end

describe "new account" do
  it_behaves_like Account
  # ...
end

describe "confirmed account" do
  it_behaves_like Account
  # ...
end

describe "blocked account" do
  it_behaves_like Account
  # ...
end

RSpec shared contexts

Shared contexts are the newest addition to the RSpec code reuse mechanisms’ spectrum (added in RSpec 2.6). You can think of them as the opposite to shared examples – while shared examples allow you to inject examples that use their host’s context (before hooks, helper methods etc.), shared contexts allow you to inject context that will be used by their host’s examples.

Let’s look at how this is done. First, we define shared context:

shared_context "shared stuff" do
  before do
    @shared_var = :some_value
  end

  def shared_method
    # ...
  end

  let(:shared_let) { :some_value }
end

Then, we include it in our example group like this:

describe "group that includes a shared context" do
  include_context "shared stuff"

  it "has access to everything defined in shared context" do
    @shared_var
    shared_method
    shared_let
  end
end

Again, there is more to it, e.g. you can include shared context via metadata instead of explicit include_context, but I skip it for the sake of example brevity.

When should you use RSpec shared contexts?

Shared contexts are mostly useful when there are several examples that share some initial state, but they are too many, too complex or too unrelated to specify them in a single describe block through nesting.

This could be to some degree achieved also by using helper methods, but helper methods and shared contexts differ a bit in scope – shared contexts are higher level, containing several different initialization elements (before hooks, let and subject methods etc.) at once, while helpers are more narrowly focused and are used rather inside these initialization elements.

Constructing RSpec examples programmatically

One more way to help you DRY your examples is to create them programmatically. This is especially useful when done in a loop. Instead of creating a lot of almost identical examples by hand:

describe "pluralize" do
  it "should map mouse to mice" do
    "mouse".pluralize.should == "mice"
  end

  it "should map cactus to cacti" do
    "cactus".pluralize.should == "cacti"
  end

  it "should map tooth to teeth" do
    "tooth".pluralize.should == "teeth"
  end
end

You can do it in a much more concise way:

describe "pluralize" do
  {"mouse" => "mice", "cactus" => "cacti", "tooth" => "teeth"}.each do |singular, plural|
    it "should map #{singular} to #{plural}" do
      singular.pluralize.should == plural
    end
  end
end

When should you create RSpec examples programmatically?

Examples can be generated programmatically when they are data-driven, that is when there are several examples that express almost identical behaviour, but with different input/output values that all need to be tested to cover all important boundary conditions.

Coding all examples for such cases explicitly, by hand is too tedious and not DRY and testing them using only a single example with a loop inside, like this:

describe "pluralize" do
  it "should map singulars to plurals" do
    {"mouse" => "mice", "cactus" => "cacti", "tooth" => "teeth"}.each do |singular, plural|
      singular.pluralize.should == plural
    end
  end
end

although very concise, will result in harder to debug tests, because the example will be stopped at the first error encountered, instead of running through all the data every time.

Final words

My final words are simple: with all the various reuse tools RSpec puts in your tool-belt, there is no excuse to allow any kind of duplication to creep into your tests. Happy and DRY coding!

PS: There is one more code reuse tool provided by RSpec: macros. However, after recent enhancements to the shared examples mechanism (passing parameters, extension blocks etc.) they are no longer necessary (see RSpec author’s notes on this topic here) so I don’t cover them in this post.

About these ads

14 Comments »

  1. [...] Different ways of code reuse in RSpec « Test Driven Websites (TDD and BDD in RoR and JS) – how to DRY out your specs [...]

  2. HI. I’ve been reading through your testing posts. I have a couple of suggestions for future articles:

    (1) I find the BDD “jump” from Cucumber to RSpec (or Test::Unit) difficult conceptually. I think your examples may be clearer that in the RSpec book! Anyway, I think that’s a great topic that is hard for people to get right.

    (2) I have been noticing that there is a code smell around putting instance variables in Cucumber steps. Maybe it’s OK for a top-level “Given” to instantiate an object under testing. Anyway, it’s interesting to me that in tests using web_steps the “state” is maintained in the browser that is being exercised (so no state in an object is set up with “new”). I wonder if there is any advice on this topic? The RSpec book does have instance variables in steps, but it never really discusses them (indeed, the actual “steps” code examples aren’t even finished in that book – there are just minimalist bits to illustrate issues).

  3. Thanks for this post! I’ve been working through learning RSpec and this little writeup was really insightful.

  4. velesin said

    @John

    Hi! Thanks for your post ideas!

    How Cucumber and Rspec tests are interwoven is explained in RSpec book (they show full BDD cycle starting with Cucumber test, then using this test for discovery of following, more low-level RSpec tests) but you’re right that this is not fully clear (I’ve also had some problems with grasping these ideas during the first read). RSpec book also omits JavaScript in this process. I think I may try to tackle this problem in one of my future posts.

    As for the second idea, I’ve planned a similar post about DRY tests (like this one) but for Cucumber (maybe even as my next post). I think that indeed it may be a good idea to include also a discussion about instance variables and maintaining state in this post.

  5. [...] Different ways of code reuse in RSpec « Test Driven Websites (TDD and BDD in RoR and JS) http://kadoppe.com/archives/2012/03/constructing-rspec-examples-by-iterator.htmlRSpecのexampleをイテレータで量産するCreativeStyle [...]

  6. [...] General [...]

  7. [...] How do we fix those? I did a little thinking and a little surfing, running across a fairly generic post on TDD. It got me thinking, I could use a helper with a shared_examples_for block and really clean up my [...]

  8. [...] I’m a big fan of keeping Rspec DRY. Mostly, because it hurts my heart to have to write the same thing twice. It huuuurts. Here’s a great article on code reuse in Rspec. [...]

  9. Igor Balos said

    Hi,

    thanks for the great article about code reuse!

    One question, though: I was wondering, if I have a shared example, with multiple examples, let’s say 3, and I would like to use two of them, is there a way to include only 2 in my test, and not the whole shared example.

    For example, if I have a shared example with following test examples in shared example:

    * it “should activate integration”
    * it “should do something”
    * it “should deactivate integration”

    I would like to include first and last example, but have the second one do something else, is that possible?

    I saw one interesting article which is dealing with this:

    http://www.mutuallyhuman.com/blog/2012/01/27/rspec-requestable-examples/

    I was wondering if this could be possible in RSpec with no extensions.

  10. [...] feel right to make this a shared example (“it behaves like…”). I came across this blog post and found the suggestion under “Constructing RSpec examples programmatically” to be [...]

  11. You can if you want to,that’s your choice.

  12. Utile Bout said

    many thanks, I just skimmed over this post, but I learned a lot, it’s really one of the best articles that I read about rspec.

  13. Thanks for the post. It was really informative!

    I have a question though. Say I had a module where I stored various helper methods to use throughout a bunch of different RSpec files. What folder/file should I store that module in?

    Thanks

  14. e cig said

    Superb post but I was wanting to know if you could write a litte more on this subject?
    I’d be very grateful if you could elaborate a little bit further.
    Bless you!

RSS feed for comments on this post · TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: