Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expectation with method missing self.class.define_method name && send(name) fails #1348

Open
ojab opened this issue Oct 22, 2020 · 2 comments

Comments

@ojab
Copy link
Contributor

ojab commented Oct 22, 2020

Subject of the issue

This https://github.com/drapergem/draper/blob/master/lib/draper/helper_proxy.rb

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "rspec", github: "rspec/rspec"
  gem "rspec-core", github: "rspec/rspec-core"
  gem "rspec-expectations", github: "rspec/rspec-expectations"
  gem "rspec-mocks", github: "rspec/rspec-mocks"
  gem "rspec-support", github: "rspec/rspec-support"
end

require 'rspec'

class ViewContext
  def xxx
  end
end

class Proxy
  def initialize
    @view_context = ViewContext.new
  end

  def method_missing(name, *args, **kwargs, &block)
    self.class.define_method(name) { @view_context.send(name) }
    send(name)
  end
end

# If this is uncommented — everything works
# Proxy.new.xxx

RSpec.describe 'fail' do
  subject { Proxy.new }

  it do
    expect(subject).to receive(:xxx).and_call_original
    subject.xxx
  end
end

RSpec::Core::Runner.invoke

Your environment

  • Ruby version: 2.6.5
  • rspec-mocks version: git main

Steps to reproduce

Run testcase

Expected behavior

It works!

Actual behavior

Failures:

  1) fail is expected to receive xxx(*(any args)) 1 time
     Failure/Error: send(name)

     SystemStackError:
       stack level too deep
     # /tmp/tst.rb:29:in `method_missing'
     # /tmp/tst.rb:29:in `method_missing'
ojab added a commit to ojab/draper that referenced this issue Oct 22, 2020
@pirj
Copy link
Member

pirj commented Oct 22, 2020

Wow, interesting find. Thanks for reporting.

@JonRowe
Copy link
Member

JonRowe commented Oct 23, 2020

This is because we "stash" the original method at the time of the expectation, to later invoke if needed, in this case it appears we stash the method missing implementation, which I don't think is the intent, so when later defined it doesn't (indeed can't) short circuit.

As an aside I would say this kind of expectation is not something we would recommend as a good idea; you're asserting that a method is called, then calling it, which in itself is pretty pointless. If you tested the side effects of calling this method you wouldn't see this bug.

ojab added a commit to ojab/draper that referenced this issue Jan 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants