也许,会有太多的代码,但我想了解为什么 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
执行第二个示例时,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
测试之间进行记忆,但没有存根方法?