RSpec 允许我 select 使用匹配器进行模拟吗?
Does RSpec allow me to select mocks with a matcher?
我打了很多电话给
Kernel.open(url).read
在一些遗留代码中,我试图在大规模重构之前模拟这些代码以进行特性测试。
我不喜欢
忽略参数的方式
allow_any_instance_of(Kernel).to(receive_message_chain(:open, :read)
.and_return(return_value))
所以我将其替换为
def stub_kernel_open_read(args: '[arg1, arg2, k: v, etc]', returning:)
allow_any_instance_of(Kernel).to(receive(:open).with(*args)).and_return(double.tap { |d|
allow(d).to(receive(:read)).and_return(returning)
})
end
但我发现我遇到了这些错误:
"http://my-fake-server/whatever" received :open with unexpected arguments
expected: ("http://my-fake-server/whatever", {:http_basic_authentication=>["asdf", "asdf"]})
got: ({:http_basic_authentication=>["asdf", "asdf"]})
Diff:
@@ -1,3 +1,2 @@
-["http://my-fake-server/whatever",
- {:http_basic_authentication=>["asdf", "asdf"]}]
+[{:http_basic_authentication=>["asdf", "asdf"]}]
Please stub a default value first if message might be received with other args as well.
所以我发现如果我将我的存根扩展到这个:
allow_any_instance_of(Kernel).to(receive(:open).with(*args)) { |instance|
return double.tap { |d|
allow(d).to(receive(:read)) {
return returning
}
}
}
然后 instance
具有 URL 的值。就目前而言这很好,我可以列出允许的 URL 列表,但感觉很糟糕。
有没有类似的
allow_any_instance_of(Kernel).that(eq('http://whatever')).to(receive(:open))
还是我完全走错了路?
显然我可以使用全局搜索替换来包装 Kernel.open(url).read
代码,并正确模拟该全局代码,但我希望尽可能避免这种情况。
AFAIU 你的问题是正确的,你需要这样的东西来告诉模拟行为 "normally":
allow(Kernel).to receive(:open).with(url).and_return(stub)
allow(Kernel).to receive(:open).with(anything).and_call_original # I can't check now, but there's a chance this one should go first, but I doubt it
然后
allow(stub).to receive(:read).and_return('something')
如果你必须模拟 Kernel.open
更多的 URL,它会变得有点乱,但原理是一样的
allow(Kernel).to receive(:open).with(first_url).and_return(first_stub)
allow(Kernel).to receive(:open).with(second_url).and_return(second_stub)
allow(Kernel).to receive(:open).with(anything).and_call_original # I can't check now, but there's a chance this one should go first
allow(first_stub).to receive(:read).and_return('something')
allow(second_stub).to receive(:read).and_return('something else')
除非我完全错过了你问题的重点?
我在 RSpec 的模拟系统中找不到任何东西来应对 'Kernel.open(url)' 方法模拟具有 url 作为实例的事实,如果你使用 allow_any_instance_of(Kernel).to(receive(:open))
。所以我做了一个小酒馆,然后意识到我不需要 'any_instance_of' 位:
def stub_kernel_open_read(url: 'fake://your_url_parameter_is_wrong', args: ['arg1', 'arg2', k: :v_etc], **rest)
allow(Kernel).to(receive(:open).with(*([url] + args)).and_return(reading_double(rest)))
end
def reading_double(return_or_raise)
double.tap do |d|
if return_or_raise[:returning]
allow(d).to(receive(:read).and_return(return_or_raise[:returning]))
else
allow(d).to(receive(:read).and_raise(return_or_raise[:raising]))
end
end
end
此代码源于试图删除使用 'any_instance' 的 receive_message_chain
。
哦,我 运行 陷入各种奇怪的恶作剧中,试图用 (*a, **b)
参数对任何方法进行存根,尽管这似乎适用于我尝试烹饪的玩具示例,所以我不知道发生了什么。正是这种神秘的废话让我反对 Ruby 和鸭子打字。
我打了很多电话给
Kernel.open(url).read
在一些遗留代码中,我试图在大规模重构之前模拟这些代码以进行特性测试。
我不喜欢
忽略参数的方式allow_any_instance_of(Kernel).to(receive_message_chain(:open, :read)
.and_return(return_value))
所以我将其替换为
def stub_kernel_open_read(args: '[arg1, arg2, k: v, etc]', returning:)
allow_any_instance_of(Kernel).to(receive(:open).with(*args)).and_return(double.tap { |d|
allow(d).to(receive(:read)).and_return(returning)
})
end
但我发现我遇到了这些错误:
"http://my-fake-server/whatever" received :open with unexpected arguments
expected: ("http://my-fake-server/whatever", {:http_basic_authentication=>["asdf", "asdf"]})
got: ({:http_basic_authentication=>["asdf", "asdf"]})
Diff:
@@ -1,3 +1,2 @@
-["http://my-fake-server/whatever",
- {:http_basic_authentication=>["asdf", "asdf"]}]
+[{:http_basic_authentication=>["asdf", "asdf"]}]
Please stub a default value first if message might be received with other args as well.
所以我发现如果我将我的存根扩展到这个:
allow_any_instance_of(Kernel).to(receive(:open).with(*args)) { |instance|
return double.tap { |d|
allow(d).to(receive(:read)) {
return returning
}
}
}
然后 instance
具有 URL 的值。就目前而言这很好,我可以列出允许的 URL 列表,但感觉很糟糕。
有没有类似的
allow_any_instance_of(Kernel).that(eq('http://whatever')).to(receive(:open))
还是我完全走错了路?
显然我可以使用全局搜索替换来包装 Kernel.open(url).read
代码,并正确模拟该全局代码,但我希望尽可能避免这种情况。
AFAIU 你的问题是正确的,你需要这样的东西来告诉模拟行为 "normally":
allow(Kernel).to receive(:open).with(url).and_return(stub)
allow(Kernel).to receive(:open).with(anything).and_call_original # I can't check now, but there's a chance this one should go first, but I doubt it
然后
allow(stub).to receive(:read).and_return('something')
如果你必须模拟 Kernel.open
更多的 URL,它会变得有点乱,但原理是一样的
allow(Kernel).to receive(:open).with(first_url).and_return(first_stub)
allow(Kernel).to receive(:open).with(second_url).and_return(second_stub)
allow(Kernel).to receive(:open).with(anything).and_call_original # I can't check now, but there's a chance this one should go first
allow(first_stub).to receive(:read).and_return('something')
allow(second_stub).to receive(:read).and_return('something else')
除非我完全错过了你问题的重点?
我在 RSpec 的模拟系统中找不到任何东西来应对 'Kernel.open(url)' 方法模拟具有 url 作为实例的事实,如果你使用 allow_any_instance_of(Kernel).to(receive(:open))
。所以我做了一个小酒馆,然后意识到我不需要 'any_instance_of' 位:
def stub_kernel_open_read(url: 'fake://your_url_parameter_is_wrong', args: ['arg1', 'arg2', k: :v_etc], **rest)
allow(Kernel).to(receive(:open).with(*([url] + args)).and_return(reading_double(rest)))
end
def reading_double(return_or_raise)
double.tap do |d|
if return_or_raise[:returning]
allow(d).to(receive(:read).and_return(return_or_raise[:returning]))
else
allow(d).to(receive(:read).and_raise(return_or_raise[:raising]))
end
end
end
此代码源于试图删除使用 'any_instance' 的 receive_message_chain
。
哦,我 运行 陷入各种奇怪的恶作剧中,试图用 (*a, **b)
参数对任何方法进行存根,尽管这似乎适用于我尝试烹饪的玩具示例,所以我不知道发生了什么。正是这种神秘的废话让我反对 Ruby 和鸭子打字。