コードが多すぎるかもしれませんが、RSpec がこのように機能する理由を理解したいと思います。double
リモート REST API とやり取りする外部オブジェクトを試しています。
コード例:
class App
class << self
def foo
external.func1
end
def bar
external.func2
end
def external
@external ||= ExternalObject.new
end
end
end
class ExternalObject
def func1
puts 'func1'
end
def func2
puts 'func2'
end
end
この仕様は機能します:
require 'rspec'
require_relative 'app'
describe App do
describe '.foo' do
it 'calls func1' do
expect_any_instance_of(ExternalObject).to receive(:func1)
described_class.foo
end
end
describe '.bar' do
it 'calls func2' do
expect_any_instance_of(ExternalObject).to receive(:func2)
described_class.bar
end
end
end
しかし、これはうまくいきません:
require 'rspec'
require_relative 'app'
describe App do
let(:external) { double('ExternalObject', func1: 1, func2: 2) }
before(:each) do
allow(ExternalObject).to receive(:new).and_return(external)
end
describe '.foo' do
it 'calls func1' do
expect(external).to receive(:func1)
described_class.foo
end
end
describe '.bar' do
it 'calls func2' do
expect(external).to receive(:func2)
described_class.bar
end
end
end
2 番目の例を実行すると、RSpec の出力double
はスタブが含まれていないように見えます。
1) App.bar calls func2
Failure/Error: described_class.bar
Double "ExternalObject" received unexpected message :func2 with (no args)
コードを次のように置き換える場合App.external
:
@external = ExternalObject.new
すべてがうまく機能します。
そのエラーの理由を検索しましたが、何も見つかりませんでした。
アップデート
また、クラスのメモ化を防ぐために、オブジェクトを複製することができます。たぶんそれは良い考えではありませんが、うまくいきます。
require 'rspec'
require_relative 'app'
describe App do
let(:external) { double('ExternalObject', func1: 1, func2: 2) }
before(:each) do
stub_const('App', App.clone)
allow(ExternalObject).to receive(:new).and_return(external)
end
describe '.foo' do
it 'calls func1' do
expect(external).to receive(:func1)
App.foo
end
end
describe '.bar' do
it 'calls func2' do
expect(external).to receive(:func2)
App.bar
end
end
end
更新 2
私は私の質問を具体化します:なぜテストの間double
にメモ化されたのに、スタブ化され@external
たメソッドがないのですか?