On Apr 26, 2011, at 4:02 PM, Rodrigo Rosenfeld Rosas wrote:

> Ok, now I understand what is your issue.
> 
>>>> class Foo
>>>> end
>>>> 
>>>> class Bar
>>>>   def self.my_foo
>>>>     @my_foo ||= Foo.new
>>>>   end
>>>>   def self.perform
>>>>     my_foo.do_something
>>>>   end
>>>> end
>>>> 
>>>> describe Foo do
>>>> 
>>>>   before(:each) do
>>>>     @stupid_mock = double('wtf')
>>>>     Foo.stub(:new => @stupid_mock)
>>>>   end
>>>>   
>>>>   it "passes here" do
>>>>     @stupid_mock.should_receive(:do_something).and_return('value')
>>>>     Bar.perform
>>>>   end
>>>>   
>>>>   it "fails here" do
>>>>     @stupid_mock.stub(:do_something => 'value')
>>>>     Bar.perform
>>>>     # double "wtf" received unexpected message :do_something with (no args)
>>>>   end
>>>>   
>>>> end
>>> 
> 
>> 
> 
> The first time Bar.foo is being called, it caches Foo.new. Since you stubbed 
> Foo.new to return a mock before running Bar.foo for the first time, this mock 
> is cached inside Bar, so any subsequent call will return the first created 
> mock object. This obviously won't happen if you run only the other spec and 
> that is why it passes in this case.
> 
> This is not about passing by reference, but it is about caching Bar.my_foo.
> 
> The only way I can think of RSpec not being affected by this is to reloading 
> all classes before each example, which would be both complicate and costly.
> 
> You'll probably have to rethink your testing strategies, avoiding caching 
> class methods or not mocking something that could affect them, or 
> initializing them first before running the specs...
> 
> Best regards,
> 
> Rodrigo.


I see now the crux of the issue - mocks are implicitly cleared out before each 
example, so even though the same object is being returned, RSpec is sneakily 
substituting a doppelganger for @stupid_mock. This apparently wasn't the case 
in the last version, where the instance variable would continue to be connected 
to the same object.

This might be intended behavior, but it feels like a bug because that memoizing 
behavior is so common.

Because I can't set up the double in a before(:all) block due to the above 
behavior, the solution is to add:
    Bar.instance_variable_set(:@my_foo, nil) # Forcibly clear Bar's cached 
instance
to the before(:each) block.

-- matt


_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to