提问人:Richard-Degenne 提问时间:8/10/2023 更新时间:8/10/2023 访问量:103
RSpec:为什么“instance_double”适用于 StandardError,而不适用于其他异常类?
RSpec: Why does `instance_double` work with StandardError, but not other exception classes?
问:
在某些测试中,我想设置一个引发特定异常类的模拟。因为这个特定的异常很难在测试中实例化,所以我想使用双精度。
下面是一个示例。
class SomeError < StandardError
def initialize(some, random, params)
# ...
end
end
class SomeClass
def some_method
mocked_method
:ok
rescue SomeError
:ko
end
def mocked_method
true
end
end
describe SomeClass do
subject(:some_class) { described_class.new }
describe '#some_method' do
subject(:some_method) { some_class.some_method }
it { is_expected.to be :ok }
context 'when #mocked_method fails' do
before do
allow(some_class).to receive(:mocked_method)
.and_raise(instance_double(SomeError))
end
it { is_expected.to be :ko }
end
end
end
但是,运行此测试失败,并显示以下消息。
Failure/Error:
mocked_method
TypeError:
exception class/object expected
奇怪的是,如果我替换为 ,它就可以正常工作。SomeError
StandardError
class SomeClass
def some_method
mocked_method
:ok
rescue StandardError
:ko
end
def mocked_method
true
end
end
describe SomeClass do
subject(:some_class) { described_class.new }
describe '#some_method' do
subject(:some_method) { some_class.some_method }
it { is_expected.to be :ok }
context 'when #mocked_method fails' do
before do
allow(some_class).to receive(:mocked_method)
.and_raise(instance_double(StandardError))
end
it { is_expected.to be :ko }
end
end
end
这是怎么回事?模拟 StandardError
时是否存在一些边缘情况?或者,有没有更好的方法来模拟难以实例化的异常类?
答:
该消息实际上来自宝石。它检查引发的错误是否通过检查。实例 double 不会对检查做出 true 响应。TypeError
pry
exception.is_a?(Exception)
is_a?
更常见的是引发错误的实例,其中包含一些参数值:
before do
allow(some_class).to receive(:mocked_method)
.and_raise(SomeError.new(1, 'a', 'b'))
end
评论
pry
问题解释
这是由 Kernel#raise
引起的。TypeError
RSpec::Mocks::MessageExpectation#and_raise
将对调用包装在稍后调用的 (如图所示) 中。Kernel#raise
Proc
Kernel#raise
接受以下内容:
- 没有参数 - “在
$!
或者如果$!
为nil
,则引发RuntimeError
“;或 - A String - “引发一个
RuntimeError
,并将字符串作为消息。或 - "一个 Exception 或其他对象,该对象在发送
异常
消息时返回Exception
对象“- 将引发此问题
Exception
在您的情况下,以上都不是,因此会抛出一个.同样可以复制:instance_double(SomeError)
Kernel#raise
TypeError
raise({a: 12})
in `raise': exception class/object expected (TypeError)
掩人耳目
原因有效不是你想的那样。StandardError
相反,看起来只是工作,因为正在救援并且是一个(继承自 )。在这种情况下,它仍在被提出,它只是在这个过程中被拯救。StandardError
SomeClass#some_method
StandardError
TypeError
StandardError
StandardError
TypeError
您可以通过更改为 (或任何其他不遵守接受的参数的参数) 来证明这一点,只要 rescues 或 ,代码仍然会通过。and_raise(instance_double(StandardError))
and_raise(instance_double(SomeError))
Kernel#raise
SomeClass#some_method
StandardError
TypeError
溶液?
虽然我不完全理解您受到的限制(例如“该特定异常很难在测试中实例化”),并且完全建议只实例化一个 ,您可以通过简单地创建一个继承自 的异常来实现您的目标作为模拟。SomeError
SomeError
class SomeError < StandardError
def initialize(some, random, params)
# ...
end
end
class MockSomeError < SomeError; def initialize(*);end; end
class SomeClass
def some_method
mocked_method
:ok
rescue SomeError
:ko
end
def mocked_method
true
end
end
describe SomeClass do
subject(:some_class) { described_class.new }
describe '#some_method' do
subject(:some_method) { some_class.some_method }
it { is_expected.to be :ok }
context 'when #mocked_method fails' do
before do
allow(some_class).to receive(:mocked_method)
.and_raise(MockSomeError)
end
it { is_expected.to be :ko }
end
end
end
评论
let
Gitlab::Error:ResponseError
。它的构造函数需要 ,而 又需要 和 ,而 和 本身需要更多的参数,依此类推。HTTParty::Response
HTTParty::Request
Net::HTTPResponse
评论