[rspec-users] Specs for ApplicationController, where to put them?

Wincent Colaiuta win at wincent.com
Wed May 30 14:36:50 EDT 2007


El 30/5/2007, a las 1:52, David Chelimsky escribió:

> I don't view that as a workaround at all. You should always be able to
> run any example file by itself. If such a file relies on other files,
> then it should require them.
>
> Another thing you could do is to put shared behaviours in spec/shared/
> and require everything in spec/shared from spec/spec_helper.
>
> The fact that the shared examples are in another file should have no
> bearing on whether they have runtime access to whatever is in the
> behaviours that include them. This all sounds odd to me. Can you post
> a backtrace please?

I've been looking at the code for this and was a bit puzzled by the  
following in lib/spec/dsl/behaviour.rb

         def add_shared_behaviour(behaviour)
           return if behaviour.equal?(found_behaviour =  
find_shared_behaviour(behaviour.description))
           ...

The intent is evidently to skip over previously instantiated shared  
behaviours, but I can't think of too many real-world cases where the  
object identity test (equal?) will succeed. About the only place it  
will succeed is in the spec (spec/spec/dsl/shared_behaviour_spec.rb):

       it "should NOT complain when adding a the same shared  
behaviour again (i.e. file gets reloaded)" do
         behaviour = behaviour_class.new("shared behaviour") {}
         behaviour_class.add_shared_behaviour(behaviour)
         behaviour_class.add_shared_behaviour(behaviour)
       end

Here the exact same instance is being added and it is naturally  
skipped. But try adding something like the following to your  
spec_helper.rb file and watch the exceptions being raised when you do  
a "rake spec":

	describe "foobar", :shared => true do
         end

Here every spec which includes the spec_helper.rb file will trigger  
an ArgumentError because the same shared behaviour gets re-evaluated  
over and over again and "found_behaviour" is set each time:

         def add_shared_behaviour(behaviour)
           return if behaviour.equal?(found_behaviour =  
find_shared_behaviour(behaviour.description))
           raise ArgumentError.new("Shared Behaviour '# 
{behaviour.description}' already exists") if found_behaviour

I think the solution here is to relax the equality comparison, use  
"==" instead of "equal?":

Index: rspec/lib/spec/dsl/behaviour.rb
===================================================================
--- rspec/lib/spec/dsl/behaviour.rb     (revision 2060)
+++ rspec/lib/spec/dsl/behaviour.rb     (working copy)
@@ -6,7 +6,7 @@
        class << self
          def add_shared_behaviour(behaviour)
-          return if behaviour.equal?(found_behaviour =  
find_shared_behaviour(behaviour.description))
+          return if behaviour == (found_behaviour =  
find_shared_behaviour(behaviour.description))
            raise ArgumentError.new("Shared Behaviour '# 
{behaviour.description}' already exists") if found_behaviour
            shared_behaviours << behaviour
          end

I've made this change against the RSpec trunk and "rake pre_commit"  
passes all specs. But I actually think that there might be some  
mistakes in the specs themselves:

       it "should NOT complain when adding a the same shared  
behaviour again (i.e. file gets reloaded)" do
         behaviour = behaviour_class.new("shared behaviour") {}
         behaviour_class.add_shared_behaviour(behaviour)
         behaviour_class.add_shared_behaviour(behaviour)
       end

The two problems I see here are:

- behaviour isn't actually a shared behaviour; you'd need to use  
"make_shared_behaviour" for that

- adding the exact same instance as in the spec is not the same as  
what happens when the file gets reloaded, because when you reload a  
file, the "describe" block is actually going to instantiate a new  
(different) Behaviour instance very time

I could change this spec to make it correct but then the following  
spec will no longer pass:

       it "should complain when adding a second shared behaviour with  
the same description" do
         make_shared_behaviour("shared behaviour") {}
         lambda { make_shared_behaviour("shared behaviour")  
{} }.should raise_error(ArgumentError)
       end

It seems to me that to get the correct behaviour this spec will have  
to be jettisoned... It's not possible to have shared behaviours in  
reloaded files without relaxing the requirement above...

Thoughts?

Cheers,
Wincent




More information about the rspec-users mailing list