class ExternalObject
attr_accessor :external_object_attribute
def update_external_attribute(options = {})
self.external_object_attribute = [1,nil].sample
end
end
class A
attr_reader :my_attr, :external_obj
def initialize(external_obj)
@external_obj = external_obj
end
def main_method(options = {})
case options[:key]
when :my_key
self.my_private_method(:my_key) do
external_obj.update_external_attribute(reevaluate: true)
end
else
nil
end
end
private
def my_private_method(key)
old_value = key
external_object.external_object_attribute = nil
yield
external_object.external_object_attribute = old_value if external_object.external_object_attribute.nil?
end
end
我想在options[:key] == :my_key
时测试以下内容main_method
:
my_private_method
使用参数:my_key
调用一次,并且它有一个块{external_obj.update_external_attribute(reevaluate: true) }
,它使用参数reevaluate: true
调用external_obj
update_external_attribute
一次。
我能够用:my_key
参数测试my_private_method
电话一次。
expect(subject).to receive(:my_private_method).with(:my_key).once
但是,如何测试期望的剩余部分呢?
谢谢
如果您也发布测试,可能会更容易回答您的问题。 设置,执行和评估/期望。
你可以在这个较老的问题中找到一个简短的答案。
您可以找到有用的阅读有关yield
匹配器的信息。
如果您还没有嘲笑ExternalObject
,我建议您嘲笑。但是除非您发布实际的测试代码,否则我无法判断。
我将回答你的问题。但是,接下来我将解释为什么你不应该这样做,并向你展示一个更好的方法。
在您的测试设置中,您需要允许双精度值屈服,以便代码将落入您的块。
RSpec.describe A do
subject(:a) { described_class.new(external_obj) }
let(:external_obj) { instance_double(ExternalObject) }
describe '#main_method' do
subject(:main_method) { a.main_method(options) }
let(:options) { { key: :my_key } }
before do
allow(a).to receive(:my_private_method).and_yield
allow(external_obj).to receive(:update_external_attribute)
main_method
end
it 'does something useful' do
expect(a)
.to have_received(:my_private_method)
.with(:my_key)
.once
expect(external_obj)
.to have_received(:update_external_attribute)
.with(reevaluate: true)
.once
end
end
end
这行得通。测试通过。RSpec 是一个强大的工具。而且,它会让你侥幸逃脱。但是,这并不意味着你应该这样做。测试私有方法总是一个坏主意。
测试应仅测试类的公共接口。 否则,您将自己锁定在当前实现中,导致在重构类的内部工作时测试失败 - 即使您没有更改对象的外部可见行为也是如此。
这是一个更好的方法:
RSpec.describe A do
subject(:a) { described_class.new(external_obj) }
let(:external_obj) { instance_double(ExternalObject) }
describe '#main_method' do
subject(:main_method) { a.main_method(options) }
let(:options) { { key: :my_key } }
before do
allow(external_obj).to receive(:update_external_attribute)
allow(external_obj).to receive(:external_object_attribute=)
allow(external_obj).to receive(:external_object_attribute)
main_method
end
it 'updates external attribute' do
expect(external_obj)
.to have_received(:update_external_attribute)
.with(reevaluate: true)
.once
end
end
end
请注意,对私有方法的期望已消失。现在,测试仅依靠class A
和class ExternalObject
的公共接口。
希望有帮助。