Есть ли способ заглушить метод включенного модуля с Rspec?

У меня есть модуль, который включен в другой модуль, и оба они реализуют один и тот же метод. Я хотел бы заглушить метод включенного модуля, примерно так:

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

Первый тест проходит, а второй выводит:

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

   expected: :bar
        got: :M

Почему в этом случае не работает заглушка? Есть ли другой способ достичь этого?

Спасибо!

------------------------------------- ОБНОВЛЕНИЕ --------- -------------------------

Спасибо! используя allow_any_instance_of (M), решил этот. Мой следующий вопрос: что произойдет, если я буду использовать preend и не включать? см. следующий код:

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 

На этот раз использование allow_any_instance_of (M) приводит к бесконечному циклу. почему это?

Ответ 1

Обратите внимание, что вы не можете напрямую вызвать M.foo! Ваш код работает только потому, что вы издевались над M.foo, чтобы вернуть :bar.

Когда вы открываете A metaclass (class << self), чтобы включить M, вы должны высмеять любой экземпляр M, добавляющий к вашему блоку before:

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