如何用RSpec模拟带块且从数组逐个yield的方法?
解决Ruby测试中模拟多次yield的问题
正确模拟多次yield的写法
你只需要链式调用多个and_yield,就能让被模拟的ranking_per_lanes方法依次yield每个lane对象,完全匹配原方法里的遍历逻辑:
# 先创建测试用的lane模拟实例 lane_l2 = double("Lane", percent: 30, tag: 'l2') lane_l1 = double("Lane", percent: 10, tag: 'l1') # 链式添加and_yield,按顺序返回需要的lane allow(Lane).to receive(:ranking_per_lanes).and_yield(lane_l2).and_yield(lane_l1) # 断言call方法的输出结果 expect(subject.call).to eq([[30, 'l2'], [10, 'l1']])
为什么之前的尝试失败?
- 直接yield整个集合是错的:原方法是逐个yield单个lane对象,不是把整个集合一次性yield出去,和原逻辑不匹配。
- 循环设置
and_yield时如果多次调用allow,会覆盖之前的设置,必须在同一个allow语句里链式拼接所有and_yield。
进阶:动态处理任意数量的lane
如果测试需要处理数量不固定的lane集合,可以用reduce动态构建yield链:
# 批量创建测试用的lane实例 test_lanes = [ double("Lane", percent: 30, tag: 'l2'), double("Lane", percent: 10, tag: 'l1') ] # 动态生成链式yield逻辑 allow(Lane).to receive(:ranking_per_lanes).then do |mock_obj| test_lanes.reduce(mock_obj) { |mock, lane| mock.and_yield(lane) } end expect(subject.call).to eq([[30, 'l2'], [10, 'l1']])
这样不管test_lanes里有多少元素,都能自动按顺序逐个yield,完美复现原方法的遍历行为。
内容的提问来源于stack exchange,提问作者lkartono
相关产品推荐
相关产品推荐

