[rspec-devel] [ rspec-Bugs-14923 ] Nested shared behaviours get executed twice

noreply at rubyforge.org noreply at rubyforge.org
Sun Oct 21 15:21:16 EDT 2007

Bugs item #14923, was opened at 2007-10-21 17:40
You can respond by visiting: 

Category: runner module
Group: None
Status: Open
Resolution: None
Priority: 3
Submitted By: Wincent Colaiuta (wincent)
Assigned to: Nobody (None)
Summary: Nested shared behaviours get executed twice

Initial Comment:
My original mailing list post follows:


Given the following ApplicationController specs:

  describe ApplicationController, "one facet", :shared => true do
    it 'foo' ...
    it 'bar' ...

  describe ApplicationController, "some other facet", :shared => true do
    it 'abc' ...
    it 'xyz' ...

  describe ApplicationController, :shared => true do
    it_should_behave_like 'ApplicationController one facet'
    it_should_behave_like 'ApplicationController some other facet'

And corresponding ApplicationController subclass specs:

  describe OtherController do
    it_should_behave_like 'ApplicationController'

Both of the shared behaviour blocks get executed twice when running the subclass specs; the specdoc output looks like:

  - foo
  - bar
  - abc
  - xyz
  - abc
  - xyz
  - foo
  - bar

And note that it's running the shared behaviours in this order:

  - 'one facet'
  - 'some other facet'
  - 'some other facet'
  - 'one facet'

Not actually a big deal; seeing as the specs don't have any side-effects and running them twice is harmless, and in any case getting rid of the nesting (putting all the specs in a single shared behaviour block) gets rid of the duplicate.

Will shortly come up with a test case that exhibits the bug. That way when this is fixed we'll have a regression test to make sure it doesn't ever creep back in.


>Comment By: Wincent Colaiuta (wincent)
Date: 2007-10-21 19:21

On exploring further I think the problem lies in at line 44 of rspec/lib/spec/dsl/shared_behaviour.rb (here I've commented it out):

      def included(mod) # :nodoc:
        example_definitions.each { |e| mod.example_definitions << e }
        before_each_parts.each   { |p| mod.before_each_parts << p }
        after_each_parts.each    { |p| mod.after_each_parts << p }
        before_all_parts.each    { |p| mod.before_all_parts << p }
        after_all_parts.each     { |p| mod.after_all_parts << p }
        #included_modules.each    { |m| mod.__send__(:include, m) }

Note that when a shared behaviour module is included, all of its included modules are explicitly included as well. This seems 
redundant, as shown in the following IRB session:

  irb> module Foo; end
  => nil
  irb> module Bar
  irb>   include Foo
  irb> end
  => Bar
  irb> Bar.included_modules
  => [Foo]
  irb> module X
  irb>   include Bar
  irb> end
  => X
  irb> X.included_modules
  => [Bar, Foo]

Notice how X includes not only Bar (which it included explicitly) but Foo as well (which it gets indirectly). Also note how the order 
of included modules is listed as "Bar, Foo"; this explains why the duplicate methods were being added in the order "A, B, B, A".

Attaching a patch which removes the redundant recursive include. Seems like a good idea as this problem will only get worse as you 
nest more and more levels deeply in nested shared behaviours. All specs still pass with this change, except for some Rails tests which 
were already broken before the changes; I suspect this breakage is unrelated and probably something to do with Edge Rails.

This patch also removes a redundant semi-colon in the same hunk of code; please let me know if you prefer this kind of unrelated 
correction to be in a separate patch.


Comment By: Wincent Colaiuta (wincent)
Date: 2007-10-21 18:23

Uploading a silly test case that demonstrates the bug.


You can respond by visiting: 

More information about the rspec-devel mailing list