Rspec中expect to receive断言对数组内对象不生效如何解决?
根本原因
问题本质是同记录多实例导致的监听失效:
- ActiveRecord 重写了
==方法,只要两个实例属于同一个类、且主键ID相同,就会返回true - RSpec的
receive断言基于内存对象的方法监听,只有断言的同一内存实例调用对应方法时才会触发。代码中@user.emails.last会触发新的数据库查询,生成新的实例,因此原有断言无法命中。
解决方案
方案1:Stub关联返回指定实例(推荐)
直接让@user.emails.last返回你已经持有的@email实例,原有断言可直接生效:
# 先stub关联的last方法,固定返回目标实例 allow(@user.emails).to receive(:last).and_return(@email) # 原有断言可以正常通过 expect(@email).to receive(:send) # 执行业务代码 @user.emails.last.send
该方案符合RSpec最佳实践,逻辑清晰,不需要对类做全局的方法监听。
方案2:用类级监听匹配记录ID
如果不方便stub关联,可以通过expect_any_instance_of监听所有Email实例的方法调用,再匹配对应记录的ID:
expect_any_instance_of(Email).to receive(:send) do |instance| # 校验调用方法的实例对应目标记录的ID即可 expect(instance.id).to eq(@email.id) end # 执行业务代码 @user.emails.last.send
小提示:如果你的场景允许提前加载关联,调用
@user.emails.load预加载集合后,@user.emails.last就会复用内存中已有的实例,此时你的原始断言也可以正常生效。不过该方法依赖关联加载状态,稳定性不如上面两个方案。
内容的提问来源于stack exchange,提问作者Andrew Sverdrup
相关产品推荐
相关产品推荐

