🧪

allow_any_instance_of を書き換える

に公開

https://www.rubydoc.info/gems/rubocop-rspec/1.7.0/RuboCop/Cop/RSpec/AnyInstance

rubocop に怒られるので書き換えるわけですが

# bad
describe MyClass do
  before { allow_any_instance_of(MyClass).to receive(:foo) }
end

# good
describe MyClass do
  let(:my_instance) { instance_double(MyClass) }

  before do
    allow(MyClass).to receive(:new).and_return(my_instance)
    allow(my_instance).to receive(:foo)
  end
end

bad のコードだと foo 以外を呼ばれた時には「元の挙動」をしてくれるのに good の書き換えは instance_double にすり替えており、 foo 以外のコールが全部エラーになる。挙動変わってんじゃん。困るんだよねそういうの。

で、正しい書き換えを伝授するのでどうぞご利用ください。

# very good
describe MyClass do
  before do
    allow(MyClass).to receive(:new).and_wrap_original do |method, *args, **kwargs, &block|
      method.call(*args, **kwargs, &block).tap do |instance|
        allow(instance).to receive(:foo)
      end
    end
  end
end

こうすると、 instance には double ではない本物のインスタンスが入りますんで、そこへ色々仕掛けることができます。

おまけ: rspec playground

https://gem.sh/gems/rspec/v3.13.1/playground

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"
  gem "rspec"
end

class Hoge
  def foo = "foo"
  def bar = "bar"
end

RSpec.describe Hoge do
  before do
    allow(Hoge).to receive(:new).and_wrap_original do |method, *args, **kwargs, &block|
      method.call(*args, **kwargs, &block).tap do |instance|
        allow(instance).to receive(:bar).and_return('stubbed')
      end
    end
  end

  it do
    hoge = Hoge.new
    expect(hoge.foo).to eq('foo')
    expect(hoge.bar).to eq('stubbed')
    puts hoge.bar
  end
end

RSpec::Core::Runner.invoke

Discussion