Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to stub a method of an included module with Rspec?

I have a module that is included in another module, and they both implement the same method. I would like to stub the method of the included module, something like this:

module M
  def foo
    :M
  end
end

module A
  class << self
    include M

    def foo
      super
    end
  end
end

describe "trying to stub the included method" do
  before { allow(M).to receive(:foo).and_return(:bar) }

  it "should be stubbed when calling M" do
    expect(M.foo).to eq :bar
  end

  it "should be stubbed when calling A" do
    expect(A.foo).to eq :bar
  end
end

The first test is passing, but the second one outputs:

Failure/Error: expect(A.foo).to eq :bar

   expected: :bar
        got: :M

Why isn't the stub working in this case? Is there a different way to achieve this?

Thanks!

-------------------------------------UPDATE----------------------------------

Thanks! using allow_any_instance_of(M) solved this one. My next question is - what happens if I use prepend and not include? see the following code:

module M
  def foo
    super
  end
end

module A
  class << self
    prepend M

    def foo
      :A
    end
  end
end

describe "trying to stub the included method" do
  before { allow_any_instance_of(M).to receive(:foo).and_return(:bar) }

  it "should be stubbed when calling A" do
    expect(A.foo).to eq :bar
  end
end 

This time, using allow_any_instance_of(M) results in an infinite loop. why is that?

like image 835
user3775153 Avatar asked Jun 25 '14 12:06

user3775153


People also ask

How do you call a module method in RSpec?

To call a module method, you specify the name of the module, and the name of the method, separated by a dot. Like this: MyModule. my_method. But you can also include modules into classes so that those classes inherit all the module's instance methods.

What is stubbing in RSpec?

In RSpec, a stub is often called a Method Stub, it's a special type of method that “stands in” for an existing method, or for a method that doesn't even exist yet. Here is the code from the section on RSpec Doubles − class ClassRoom def initialize(students) @students = students End def list_student_names @students.

What is allow in RSpec?

Use the allow method with the receive matcher on a test double or a real. object to tell the object to return a value (or values) in response to a given. message. Nothing happens if the message is never received.


1 Answers

Note you cannot directly call M.foo! Your code only seems to work because you mocked M.foo to return :bar.

When you open A metaclass (class << self) to include M, you have to mock any instance of M, that is adding to your before block:

allow_any_instance_of(M).to receive(:foo).and_return(:bar)

module M   def foo     :M   end end  module A   class << self     include M      def foo       super     end   end end  describe "trying to stub the included method" do   before do     allow(M).to receive(:foo).and_return(:bar)     allow_any_instance_of(M).to receive(:foo).and_return(:bar)   end     it "should be stubbed when calling M" do     expect(M.foo).to eq :bar   end    it "should be stubbed when calling A" do     expect(A.foo).to eq :bar   end end 
like image 158
mdemolin Avatar answered Oct 06 '22 01:10

mdemolin