Home > Software engineering >  RSpec stub method with block that yield from an array
RSpec stub method with block that yield from an array

Time:09-12

I have a method which map a collection of object...

def ranking_per_lanes
  all.map do |lane|
    yield(lane) if block_given?
  end
end

...and yield each item if a block has been passed:

def call
  ranking_per_lanes do |lane|
    [lane.percent, lane.tag]
  end
end

# Output
[
  [30, 'l2'],
  [10, 'l1']
]

Here I'm trying to test call method by mocking ranking_per_lanes but struggle to achieve it. I know how to mock a method in order to yield a single lane:

allow(Lane).to receive(:ranking_per_lanes).and_yield(lane)

However, how does one reproduce what the map block is doing ? I tried to do the following but it yield the entire collection:

allow(Lane).to receive(:ranking_per_lanes).and_yield([
  FactoryBot.create(:lane, tag: 'l2', percent: 30),
  FactoryBot.create(:lane, tag: 'l1', percent: 10)
])

I also tried to loop on the collection and mock it then:

[
  FactoryBot.create(:lane, tag: 'l2', percent: 30),
  FactoryBot.create(:lane, tag: 'l1', percent: 10)
].each do |lane|
  allow(Lane).to receive(:ranking_per_lanes).and_yield(lane)
end

but it didn't work either :(

Thanks a lot for your help

CodePudding user response:

I found a solution to my problem. It is possible to stub a method with a substitue in order to pass it an internal logic. In my case, here is what I ended up doing:

before(:each) do
  allow(Lane).to receive(:ranking_per_lanes) do |&block|
    [
      FactoryBot.create(:lane, tag: 'l2', percent: 30),
      FactoryBot.create(:lane, tag: 'l1', percent: 10)
    ].map(&block)
  end
end

which allow me to test properly my call method:

it 'returns an array of lanes with their score' do
  expect(service.send(:call)).to eq(
    [
      [30, 'l2'],
      [10, 'l1']
    ]
  )
end
  • Related