Ric*_*nne 4 ruby rspec exception mocking
在某些测试中,我想设置一个引发特定异常类的模拟。因为该特定异常很难在测试中实例化,所以我想使用双精度。
这是一个例子。
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
Run Code Online (Sandbox Code Playgroud)
但是,运行此测试失败并显示以下消息。
Failure/Error:
mocked_method
TypeError:
exception class/object expected
Run Code Online (Sandbox Code Playgroud)
奇怪的是,如果我替换SomeError为StandardError,它就可以正常工作。
Failure/Error:
mocked_method
TypeError:
exception class/object expected
Run Code Online (Sandbox Code Playgroud)
这里发生了什么?嘲笑时是否有一些边缘情况StandardError?或者,是否有更好的方法来模拟难以实例化的异常类?
问题说明
其TypeError原因是Kernel#raise.
RSpec::Mocks::MessageExpectation#and_raise将调用包装在稍后调用的Kernel#raisea Proc(如图所示)中。
Kernel#raise接受以下内容:
$!或引发RuntimeErrorif $!is nil”;或者RuntimeError字符串作为消息引发。” ; 或者Exception或另一个对象”Exceptionexception -Exception将引发此问题在您的情况下,instance_double(SomeError)以上都不是,因此Kernel#raise会抛出TypeError. 同样的内容可以通过以下方式重现:
raise({a: 12})
in `raise': exception class/object expected (TypeError)
Run Code Online (Sandbox Code Playgroud)
红鲱鱼
起作用的原因StandardError并不是你想象的那样。
相反,StandardError仅仅看起来有效,因为SomeClass#some_method正在救援StandardError并且TypeError是StandardError(继承自StandardError)。在这种情况下,它TypeError仍在被提升,它只是在这个过程中被营救。
您可以通过更改and_raise(instance_double(StandardError))为and_raise(instance_double(SomeError))(或任何其他不符合 接受的参数的参数Kernel#raise)来证明这一点,并且只要SomeClass#some_method救援StandardError或 ,代码仍然会通过TypeError。
解决方案?
虽然我不完全理解您所面临的限制(例如“该特定异常很难在测试中实例化”),并且完全建议仅实例化 a ,但您可以通过简单地创建一个继承自asSomeError的 Exception 来实现您的目标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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
181 次 |
| 最近记录: |