0

So i'm experiencing this weird behavior while testing a ruby class. I'm using rspec 3 to test it by the way.

Class Foo has a method 'fetch_object' which calls the 'find' method from class Bar to retrieve an object and than calls the method 'fail' from the fetched object.

The so called weird behavior happens when i expect to receive the method 'fail' once and receive none but if I change the method name for 'faill' it works like a charm :S

here is the drama:

require 'ostruct'

class Foo

  def fetch_object
    foobar = Bar.find
    foobar.fail
  end

end

class Bar

  def self.find
    OpenStruct.new(name: 'Foo Bar')
  end

end

describe Foo do

  subject { Foo.new }

  let(:foo) { OpenStruct.new() }

  before do
    expect(Bar).to receive(:find).and_return(foo)
  end

  it 'fetch object with name' do
    expect(foo).to receive(:fail)
    subject.fetch_object
  end

end
1
  • when I run your code I get Failure/Error: expect(Bar).to receive(:find).and_return(foo) (<Bar (class)>).find(any args) expected: 1 time with any arguments received: 0 times with any arguments Commented Oct 2, 2014 at 0:33

1 Answer 1

2

I suspect it's because you are setting an expectation on object, which behaviour depends on method_missing (OpenStruct).

For that reason I wouldn't want it as a mock, I would use regular mock (and spec will pass):

let(:foo) { double('foobar') }

You are testing here, if returned object (result of Bar.find) will receive an expected message, without going into implementation details.

Setting expectations on Dynamic classes like ostruct may lead to strange results. It seems that at some point a Kernel#fail method is invoked, thus, changing a name to faill or any other that is not already "taken" by Kernel will make it work.

Other solution would be monkeypatching OpenStruct to avoid method_missing beeing called:

class OpenStruct
  def fail
    true
  end
end

class Foo

  def fetch_object
    foobar = Bar.find
    foobar.fail
  end

end

class Bar

  def self.find
    OpenStruct.new(name: 'Foo Bar')
  end

end

describe Foo do

  subject { Foo.new }

  let(:foo) { OpenStruct.new }

  before do
    expect(Bar).to receive(:find).and_return(foo)
  end

  it 'fetch object with name' do
    expect(foo).to receive(:fail)
    subject.fetch_object
  end

end

But I don't know why would you want to do that ;)

More info: Doubles and dynamic classess

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.