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

#ruby #rspec #mixins #stubbing

#ruby #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) решило эту проблему. Мой следующий вопрос — что произойдет, если я использую prepend, а не include? смотрите следующий код:

 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 над return :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
  

Комментарии:

1. Это работает. По-видимому, в отличие allow от, allow_any_instance_of не требует определения метода для объекта.

2. я думаю, это просто помогло мне решить проблему, связанную с более чем 5 часами ударов головой о стол. Спасибо!

3. Ср., expect_any_instance_of … это навело меня на правильный путь