RSpec中如何按对象身份校验have_received方法的调用参数?
根本原因
该问题由 Ruby Comparable 模块特性与 RSpec 参数匹配默认规则共同触发:
- 类混入
Comparable模块后,Ruby 会自动基于实现的<=>方法生成默认==相等判断逻辑:只要两个实例的<=>返回0,==就返回true,因此示例中val相等的两个不同B类实例b1 == b2结果为true。 - RSpec 的
with参数匹配器默认使用==做相等校验,因此会把两次传入val相等的B实例的调用都判定为匹配,导致调用计数为2,不符合预期的1次,测试报错。
解决方案
方案1:使用RSpec身份匹配器(推荐,无需修改业务代码)
如果测试需要严格匹配同一个对象实例,不需要改动业务逻辑,只需将with的参数替换为equal匹配器即可。equal基于object_id判断是否为同一实例,不受自定义==方法影响:
# 修改后的测试代码 expect(s).to have_received(:call).with(equal(b1)).exactly(1).times expect(s).to have_received(:call).with(equal(b2)).exactly(1).times
也可以用功能完全一致的be匹配器代替,写法为with(be(b1))。
方案2:重写类的==方法(仅当业务逻辑需要区分实例相等时使用)
如果业务场景下,即使val相同,不同的B实例也应该判定为不相等,可以显式重写B类的==方法,覆盖Comparable生成的默认逻辑:
class B include Comparable attr_reader :val def initialize(val) @val = val end def <=>(other) self.val <=> other.val end # 显式重写==,要求必须是同一个实例才相等 def ==(other) self.object_id == other.object_id end end
修改后原有测试代码无需改动即可正常运行。
内容的提问来源于stack exchange,提问作者Lam Phan
相关产品推荐
相关产品推荐

