rspec / rspec-mocks

RSpec's 'test double' framework, with support for stubbing and mocking
https://rspec.info
MIT License
1.16k stars 359 forks source link

An exception is raised when invoking an `any_instance` expectation on a subclass that has an `any_instance` stub on its parent class #1077

Open joshuap opened 8 years ago

joshuap commented 8 years ago

Re: https://twitter.com/joshuap/status/722545431104753664 cc @samphippen

To reproduce this bug check out my gist:

git clone git@gist.github.com:4561dff8af653d1f6a3862557d15c503.git rspec_bug
cd rspec_bug
bundle install
bundle exec rspec bug_spec.rb

Example:

class ExampleBase
  def foo(arg)
    true
  end
end

class ExampleObj < ExampleBase
  def foo(arg)
    super
  end
end

context "when subclass is object of expect_any_instance_of" do
  before do
    allow_any_instance_of(ExampleBase).to receive(:foo)
  end

  it "blows up" do
    expect_any_instance_of(ExampleObj).to receive(:foo).with(:bar)
    ExampleObj.new.foo(:bar)
  end
end

Resulting error:

     NameError:
       method `__foo_without_any_instance__' not defined in ExampleObj
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/any_instance/recorder.rb:197:in `remove_method'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/any_instance/recorder.rb:197:in `block in restore_original_method!'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/any_instance/recorder.rb:194:in `class_exec'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/any_instance/recorder.rb:194:in `restore_original_method!'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/any_instance/recorder.rb:184:in `restore_method!'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/any_instance/recorder.rb:144:in `stop_observing!'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/any_instance/recorder.rb:230:in `observe!'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/any_instance/recorder.rb:63:in `should_receive'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/any_instance/proxy.rb:80:in `perform_proxying'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/any_instance/proxy.rb:62:in `should_receive'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/matchers/receive.rb:96:in `setup_method_substitute'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/matchers/receive.rb:89:in `setup_any_instance_method_substitute'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/matchers/receive.rb:45:in `setup_any_instance_expectation'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/targets.rb:45:in `define_matcher'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.4.1/lib/rspec/mocks/targets.rb:14:in `block in delegate_to'
     # ./bug_spec.rb:19:in `block (2 levels) in <top (required)>'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/example.rb:236:in `instance_exec'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/example.rb:236:in `block in run'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/example.rb:478:in `block in with_around_and_singleton_context_hooks'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/example.rb:435:in `block in with_around_example_hooks'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/hooks.rb:478:in `block in run'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/hooks.rb:616:in `run_around_example_hooks_for'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/hooks.rb:478:in `run'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/example.rb:435:in `with_around_example_hooks'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/example.rb:478:in `with_around_and_singleton_context_hooks'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/example.rb:233:in `run'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/example_group.rb:581:in `block in run_examples'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/example_group.rb:577:in `map'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/example_group.rb:577:in `run_examples'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/example_group.rb:543:in `run'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/runner.rb:119:in `block (3 levels) in run_specs'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/runner.rb:119:in `map'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/runner.rb:119:in `block (2 levels) in run_specs'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/configuration.rb:1680:in `with_suite_hooks'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/runner.rb:118:in `block in run_specs'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/reporter.rb:77:in `report'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/runner.rb:117:in `run_specs'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/runner.rb:93:in `run'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/runner.rb:78:in `run'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/lib/rspec/core/runner.rb:45:in `invoke'
     # /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.4/exe/rspec:4:in `<top (required)>'
     # /usr/local/var/rbenv/versions/2.2.2/bin/rspec:23:in `load'
     # /usr/local/var/rbenv/versions/2.2.2/bin/rspec:23:in `<main>'
fables-tales commented 8 years ago

Thanks for the clear issue @joshuap! I'm convinced this is a bug, but I don't have the time to work out the root cause right now. Hopefully more info soon.

fables-tales commented 8 years ago

OK, my repro isn't quite right

fables-tales commented 8 years ago

1a7e923 is the actual commit which reproduces this issue.

I am really struggling with git today. Sorry everyone.