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

Already on GitHub? Sign in to your account

"unexpected method invocation" with ActiveRecord association #59

Open
ono opened this Issue May 17, 2011 · 2 comments

Comments

Projects
None yet
2 participants
Contributor

ono commented May 17, 2011

If you have ActiveRecord classes (rails 2.x) like the following:

class Blog < ActiveRecord::Base
  has_many :articles
end

class Article < ActiveRecord::Base
  belongs_to :blog
end

Then if you have a spec like this:

it "RR should raise 'unexpected method invocation' error" do
  # @article: an article object
  mock(@article.blog).name { "A Blog" }
  @article.blog.name.should == "A Blog"  # This works.
  @article.blog.url.should == "http://original.url.com"  # Invokes original method. Good.
  @article.blog.url  # RR raises 'unexpected method invocation' unexpectedly here!!
end

It seems that RR doesn't like ActiveRecord::Associations::AssociationProxy which proxies activerecord object of the association through method_missing like bellow:

def method_missing(method, *args, &block)
  if load_target                         
    if @target.respond_to?(method)       
      @target.send(method, *args, &block)
    else                                 
      super                              
    end                                  
  end                                    
end                                      

I wrote a spec which reproduces this issue on my forked repo. Could you have a look?

ono/rr@70d8174

Contributor

ono commented May 18, 2011

FYI you could avoid this issue with mocking the target directly.

e.g.

mock(@article.blog.proxy_target).name { "A Blog" }

Just a workaround though.

Collaborator

mcmire commented Mar 8, 2013

I can confirm this is still an issue with 1.4.0 / my fork:

require 'rr'

require 'rspec/core'
require 'rspec/expectations'
RSpec.configure do |c|
  c.mock_with :nothing
end

# The ProxySample class imitates AssociationProxy class in ActiveRecord.
class ProxySample
  def initialize
    @model = ModelSample.new
  end

  def method_missing(sym,*args,&block)
    if @model.respond_to?(sym)
      @model.send(sym, *args, &block)
    else
      super
    end
  end
end

# The ModelSample class imitates ActiveRecord::Base class in ActiveRecord.
class ModelSample
  def name
    "Brian Takita"
  end

  def github_id
    "btakita"
  end
end

describe "with a proxy class" do
  include RR::Adapters::RSpec2

  before do
    @proxy = ProxySample.new
    mock(@proxy).name.times(1) { "Mocked!" }
  end

  it "mocks properly usually" do
    @proxy.name.should == "Mocked!"
  end

  it "calls an original method if it is not mocked" do
    @proxy.name.should == "Mocked!"
    @proxy.github_id.should == "btakita"
  end

  it "calls an original method if it is not mocked for the second time too " do
    @proxy.name.should == "Mocked!"
    @proxy.github_id.should == "btakita"
    @proxy.github_id.should == "btakita"
  end
end

Output:

$ ruby -I ~/code/github/forks/rr/lib -S rspec -f documentation /tmp/rr-test.rb

with a proxy class
  mocks properly usually
  calls an original method if it is not mocked
  calls an original method if it is not mocked for the second time too (FAILED - 1)

Failures:

  1) with a proxy class calls an original method if it is not mocked for the second time too
     Failure/Error: @proxy.github_id.should == "btakita"
     RR::Errors::DoubleNotFoundError:
       On subject #<ProxySample:0x007ffa22342cd0>,
       unexpected method invocation:
         github_id()
       expected invocations:
     # /tmp/rr-test.rb:60:in `block (2 levels) in <top (required)>'

Finished in 0.0044 seconds
3 examples, 1 failure

Failed examples:

rspec /tmp/rr-test.rb:57 # with a proxy class calls an original method if it is not mocked for the second time too
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment