最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ruby - RSpec -- re-evaluate allow .and_yield when mocked method is called - Stack Overflow

programmeradmin1浏览0评论

In this code, sync_external accepts an array of posts and modifies it by calling a sync method from an external module ExternalService, which is mocked in the test.

Assume there's some reason to pass a block to sync. In this trivial example, the block exists to push every yielded response onto a responses array.

Code:

class Post
  def self.sync_external(posts)
    responses = []
    ExternalService.sync(posts) do |response|
      responses << response
    end
    return responses
  end
end

RSpec:

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

Actual result:

allow evaluates in the before block when the responses array was empty, so it yielded nothing.

Expected result:

allow evaluates in the before block, but re-evaluates and_yield when the mocked method is called. This will let each it assign different responses, while keeping the allow mock in the before block.

Edit (Solution): Passing in a block argument and calling the block multiple times is equivalent to chaining .and_yield multiple times, but useful for evaluating other RSpec variables at runtime.

allow(ExternalService).to receive(sync).with(anything) do |_, &b|
  responses.each{b.call(_1)}
end

Thanks to @engineersmnky for his solution here:

发布评论

评论列表(0)

  1. 暂无评论