Until sometime very soon, when you describe a module in RSpec using this syntax:

describe SomeModule do
  ...
end

RSpec implicitly includes that module in the example group. This allows you to do this:

describe CatLikeBehaviour do
  it "should say 'meow' when it greets you" do
    say_hello.should == 'meow'
  end
end

module CatLikeBehaviour do
  def say_hello
    'meow'
  end
end

As is often the case with things implicit, this actually turns out to be a problem. The problem revealed itself most notably when an RSpec user reported that a describe() method in a module he was using was conflicting with RSpec’s describe() method.

One response to that thread suggested that using describe() in a module might be too generic, but I think that really hides the point. Imagine how frustrated you would get if you had examples of a module with a current? method and we decided to add a current? method to Spec::Example::ExampleGroupMethods. You’d suddenly start seeing those examples fail with stack traces eminating from RSpec instead of your code. Not good.

And so, we are going to be removing this feature.

With the 1.1.4 release, you get a warning any time that the example calls a method on self that is part of the included module. Soon it will be removed entirely.

rails helper examples

The biggest impact of this is going to be felt in rspec-rails helper examples. There are two remedies that you have if you’ve got examples that send messages to self that should be going to another object:

1. use the new helper object provided by HelperExampleGroup

describe DateHelper do
  it "should format the date as mm/dd/yyyy" do
    helper.format_date(Date.new(2008, 5, 31)).should == '05/31/2008'
  end
end

The helper object is an instance of ActionView::Base with the named module included in it, so it has access to everything else that it should have.

2. include the module explicitly

describe DateHelper do
  include DateHelper
  it "should format the date as mm/dd/yyyy" do
    format_date(Date.new(2008, 5, 31)).should == '05/31/2008'
  end
end

My recommendation is definitely the first option as I find it more expressive.

I realize this is API changing and backward-compatibility breaking, but this is one of those cases where, at least in my view, the pain is justified by the result.

8 Responses to “RSpec waving 'bye bye' to implicit module inclusion”

  1. Chris Kilmer
    Chris Kilmer Says:

    “I realize this is API changing and backward-compatibility breaking…”

    While this may be true, I don’t personally think your are wrong. You could almost view this as a usage bugs.

    Bugs need to be fixed :-)

  2. Matti Paksula
    Matti Paksula Says:

    Yesterday I was thinking that using object named helper should be the way to do helper testing, great timing! I think this change is good even if some fixing is needed on my tests.

  3. Simon B.
    Simon B. Says:

    What if describe could detect method calls in the do-block that should’ve had helper prepended to them, and just route those over to helper automagically?

  4. Simon B.
    Simon B. Says:

    What if describe could detect method calls in the do-block that should’ve had helper prepended to them, and just route those over to helper automagically?

  5. Dan Manges
    Dan Manges Says:

    +1 on this change, and +1 on favoring option #1 under the rails helper example. For modules that aren’t rails helpers, it’s easy enough to use the same approach.

    sample = Class.new { include ModuleToTest }.new
  6. Michael
    Michael Says:

    Whenever I make this change I seem to be getting “The error occurred while evaluation nil.url_for” in all my named routes in the specs. Is the preferred method of doing routes in specs now to stub them out in the helper object?

  7. David Chelimsky
    David Chelimsky Says:

    @Michael – actually, that’s a bug. For right now you’d be best just ignoring the warnings. I’ll get a fix in shortly.

  8. David Chelimsky

Sorry, comments are closed for this article.