On Apr 25, 2011, at 10:51 AM, Doug McInnes wrote:

> Sure!
> Here's the Gist:
> 
> https://gist.github.com/940868
> 
> It's works in Rspec 2.5.1, but not in Rspec 1.3.2
> 
> Doug
> 
> 
> On Apr 23, 2011, at 3:00 PM, David Chelimsky wrote:
> 
>> On Apr 22, 2011, at 4:58 PM, Doug McInnes wrote:
>> 
>>> Hi!
>>> 
>>> I was just talking to @dchelimsky over Twitter about a weird corner case 
>>> we've run into on 1.3.1 (I've also been able to reproduce it in 1.3.2)
>>> 
>>> So we're using this gem called ClassyStruct that's a higher performing 
>>> OpenStruct:
>>> https://github.com/amikula/classy_struct
>>> 
>>> Basically it acts the same as OpenStruct but defines methods on the 
>>> object's class instead of the object itself on the fly.
>>> When it receives a call that hits method_missing it calls attr_accessor on 
>>> the method name then passes the call on to the object.
>>> 
>>> Our problem comes from having one spec that stubs out a call to the object:
>>> foo.stub! :bar => 'test'
>>> 
>>> and later in another spec file trying to set the same method with a value 
>>> then having our code use that value:
>>> # spec
>>> foo.bar = 'other test'
>>> # code
>>> puts "#{foo.bar} baz"
>>> 
>>> So our expectation is that foo.bar will return 'other test'. Instead it 
>>> hits the old stub on foo which calls method_missing which is picked up 
>>> again by ClassyStruct causing it to fire off attr_accessor again then 
>>> passing the method through causing the stub to call method_missing and on 
>>> and on finally giving us a "stack level too deep" error.
>>> 
>>> The crux of the problem is that ClassyStruct is adding a method to the 
>>> class after Rspec has added the same method to the instance.
>>> 
>>> As I said it's a very weird corner case because we're calling attr_accessor 
>>> on a class that already has objects floating around. The easiest way to fix 
>>> this is to use stub! in both places.
>>> 
>>> Regardless we were surprised that the proxy sticks around after a test run. 
>>> What is the reason for keeping it around?
>> 
>> There's no intent to keep it around, so there is a bug at play here, but 
>> let's see if we can narrow it down.
>> 
>> Can you post (gist or pastie) an example that I can just run as/is to see 
>> the behavior you're seeing?
> 


Any ideas on this issue?

Here's the code:

class TestClass
end

# our use case is with a stub on a constant
TEST = TestClass.new

describe TestClass do
  it "works before the stub" do
    TestClass.send(:attr_accessor, :foo)
    TEST.foo = :baz

    TEST.foo.should == :baz

    TestClass.send(:remove_method, 'foo')
    TestClass.send(:remove_method, 'foo=')
  end

  it "has a stubbed method" do
    TEST.stub! :foo => :bar

    TEST.foo.should == :bar
  end

  it "fails after the stub" do
    TestClass.send(:attr_accessor, :foo)
    TEST.foo = :baz

    TEST.foo.should == :baz
  end
end



And the console output:

~/tmp $ ruby -v
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.4.0]
~/tmp $ spec -v
rspec 1.3.2
~/tmp $ spec test.rb 
..F

1)
NoMethodError in 'TestClass fails after the stub'
undefined method `foo' for #<TestClass:0x00000101180158>
test.rb:28:in `block (2 levels) in <top (required)>'

Finished in 0.046678 seconds

3 examples, 1 failure
~/tmp $ rspec -v
2.5.1
~/tmp $ rspec test.rb 
...

Finished in 0.00154 seconds
3 examples, 0 failures


As I said this is a weird corner case :)

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

Reply via email to