Rails中RSpec的mock/stub测试为何expect放最后行会失败
问题核心原因
RSpec 的消息预期语法 expect(obj).to receive(:method) 属于事前监听规则,作用是提前告知 RSpec 监听指定对象的方法调用:后续执行过程中如果该对象被调用了符合参数要求的指定方法则断言通过,从未被调用则断言失败。
你将该语句放在 payment.save 执行之后才声明,等于 logger.record_payment 已经完成调用,你才告诉 RSpec 要监听该方法的调用,RSpec 没有捕获到对应调用记录,自然会抛出「预期 LoggerA#record_payment 被调用1次,实际调用0次」的错误。
如何实现「先执行逻辑、后验证结果」的写法
如果习惯把断言放在业务逻辑之后,可以用 RSpec 的 Spy 模式实现事后校验调用记录,修改后的测试代码如下:
describe Payment do it 'records the payment' do payment_gateway = double() allow(payment_gateway).to receive(:charge).and_return(payment_id: 1234) # 用spy方法创建支持事后校验的测试替身 logger = spy('LoggerA') payment = Payment.new(payment_gateway, logger) payment.total_cents = 1800 payment.save # 用have_received匹配器做事后调用记录校验 expect(logger).to have_received(:record_payment).with(1234) end end
两种消息预期的区别总结
expect(obj).to receive(:method):事前声明型,会替换原方法实现,必须放在业务代码执行前,适合直接做 mock 拦截expect(obj).to have_received(:method):事后校验型,要求测试替身为 spy 类型,放在业务代码执行后使用,符合常规的「先执行、后断言」测试逻辑
内容的提问来源于stack exchange,提问作者Bình Trương
相关产品推荐
相关产品推荐

