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

32,085

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
Share:
32,085
Admin
Author by

Admin

Updated on July 25, 2022

Comments

  • Admin
    Admin almost 2 years

    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?

  • Peter Ehrlich
    Peter Ehrlich over 8 years
    This works. Apparently, unlike allow, allow_any_instance_of doesn't require the method to be defined on the object.
  • Derek
    Derek about 8 years
    i think this just helped me solve 5+ hours worth of banging my head on the table. thanks!
  • Daniel Bidulock
    Daniel Bidulock almost 8 years
    Cf., expect_any_instance_of... this put me on the right track