RSpec:调用被Mock方法时如何重新计算allow的.and_yield?
RSpec Mock 带块方法的动态 Yield 问题解决
业务代码实现
Post.sync_external 方法接收posts数组,调用外部模块 ExternalService.sync 并传入块,将该方法yield的每个响应存入responses数组后返回:
class Post def self.sync_external(posts) responses = [] ExternalService.sync(posts) do |response| responses << response end return responses end end
初始测试代码
尝试通过Mock ExternalService.sync 模拟yield行为,但存在逻辑问题:
let(:responses) {[]} before do allow(ExternalService).to receive(sync).with(anything) .then{responses.inject(_1){|m, response| m.and_yield(response)}} end it "sync posts with 100 words successfully" do posts = FactoryBot.build_list(:post, 10, words: 100) posts.each{|post| responses << 200} expect(Posts.sync_external(posts)).to eq(Array.new(10, 200)) end
问题说明
- 实际结果:
before块执行时responses数组为空,导致Mock的sync方法不会yield任何内容,测试无法通过。 - 期望效果:Mock配置保留在
before块中,但调用Mock方法时能动态计算要yield的内容,让每个it块可灵活设置不同响应。
解决方法
通过在Mock的实现中直接接收块参数,遍历 responses 数组并调用传入的块,实现动态yield:
allow(ExternalService).to receive(sync).with(anything) do |_, &b| responses.each{b.call(_1)} end
这样每次调用 ExternalService.sync 时,都会实时遍历当前的 responses 数组,将每个元素通过块yield出去,满足测试中动态设置响应的需求。
内容的提问来源于stack exchange,提问作者Levvine
相关产品推荐
相关产品推荐

