You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 16:25:09