我想确保我的EventNotifier
对象捕获并将异常记录到Rails记录器中,因此我使用rspec-mocks编写了以下rspec测试:
describe EventNotifier do
before(:all) do
RSpec::Mocks::setup(self)
end
it 'logs exception on error' do
error = RuntimeError.new('simulated for tests')
EventDispatcher.should_receive(:dispatch).with('a message').and_raise(error)
Rails.logger.should_receive(:warning).with(error)
subject.notify('a message')
end
end
class EventDispatcher
def dispatch(message)
# stuff
end
end
class EventNotifier
def notify(message)
EventDispatcher.dispatch(message)
rescue RuntimeError => e
Rails.logger.warning(e)
end
end
测试通过了铃声和口哨,但我犯了大错误。你能发现它吗?实际上,记录警告的Rails方法是warn
,而不是warning
。因此,当用于实际代码时,此代码将失败。
有没有办法让rspec-mock禁止原始对象上不存在的模拟方法?
另一个有用的例子:如果我决定将EventDispatcher#dispatch
重命名为EventDispatcher#route
,那么测试仍会通过。如果它是java,它将在编译时失败,因为接口已更改。使用rspec-mock,我不知道如何让它自动失败。
答案 0 :(得分:0)
rspec-mocks 3.0可以引入verifying doubles:
验证双打是提供正常双打的更严格的替代方案 关于正在验证的内容的保证。 使用验证双打时,RSpec 将检查被存根的方法是否实际存在于 基础对象(如果可用)。更喜欢使用验证双打 正常的双打。
所以给定这些类(来自spec的例子):
class ConsoleNotifier
def notify(msg)
puts message
end
end
class User < Struct.new(:notifier)
def suspend!
notifier.notify("suspended as")
end
end
此示例将通过,因为实际notify
实例存在方法ConsoleNotifier
:
describe User, '#suspend!' do
it 'notifies the console' do
notifier = instance_double("ConsoleNotifier")
expect(notifier).to receive(:notify).with("suspended as")
user = User.new(notifier)
user.suspend!
end
end
如果将notify
方法重命名为publish
,则该示例将失败,因为期望在方法notify
上,并且该方法在目标对象上不存在。
# would make example fail
class ConsoleNotifier
def publish(msg)
puts message
end
end
如果我们在color
方法中添加notify
参数,它也会失败,因为方法arity在期望中是错误的(参数数量错误)。
class ConsoleNotifier
def notify(msg, color)
puts color + message
end
end
在对课程提出期望时,例如EventDispatcher.should_receive(:dispatch)
或expect(EventDispatcher).to receive(:dispatch)
,创建的double在rspec-mocks词汇表中命名为partial double。
要使部分双打像验证双打一样,必须设置verify_partial_doubles
配置选项:
RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
它会自动确保EventDispatcher
类具有dispatch
方法。因此,如果重命名此方法,测试将失败,正如预期的那样!