Keyword argument error on Ruby 3.0 for #receive
Created by: swiknaba
Subject of the issue
When passing a block to#receive
, it does not yield kwargs correctly but instead wraps kwargs in an array.
Your environment
- Ruby version: 3.0.0
- rspec-mocks version: 3.10.1
Steps to reproduce
# Gemfile
source 'https://rubygems.org'
gem 'rspec'
ruby '3.0.0'
run rspec --init
# spec/receive_spec.rb
class Maker
def do_stuff(thing: nil)
puts "thing from kwargs: #{thing}"
end
end
class Caller
def self.prepare(_arg, maker: nil)
clerk = new(maker: maker)
yield(clerk)
end
def initialize(maker: nil)
@maker = maker
end
def call_maker(thing: nil)
@maker.do_stuff(thing: thing)
end
end
require_relative 'spec_helper' # just the boilerplate from rspec --init
describe Caller do
let(:the_thing) { 'test' }
let(:maker) { Maker.new }
# fails in Ruby 3.0.0
it 'hands the thing to the maker (kwargs)' do
expect(maker).to receive(:do_stuff) do |**kwargs|
expect(kwargs[:thing]).to eq(the_thing)
end.exactly(1).times
described_class.prepare('bar', maker: maker) do |clerk|
clerk.call_maker(thing: the_thing)
end
end
it 'hands the thing to the maker (args)' do
expect(maker).to receive(:do_stuff) do |*args|
expect(args.first[:thing]).to eq(the_thing)
end.exactly(1).times
described_class.prepare('bar', maker: maker) do |clerk|
clerk.call_maker(thing: the_thing)
end
end
end
-> run rspec
.
Ruby 2.7.2: all specs are passing
1) Caller hands the thing to the maker (kwargs)
Failure/Error: expect(kwargs[:thing]).to eq(the_thing)
expected: "test"
got: nil
(compared using ==)
# ./spec/receive_spec.rb.rb:30:in `block (3 levels) in <top (required)>'
# ./spec/receive_spec.rb.rb:18:in `call_maker'
# ./spec/receive_spec.rb.rb:34:in `block (3 levels) in <top (required)>'
# ./spec/receive_spec.rb.rb:10:in `prepare'
# ./spec/receive_spec.rb.rb:33:in `block (2 levels) in <top (required)>'
Expected behavior
When passing a block to receive
, I can pass kwargs and have those available inside the block.
Actual behavior
Kwargs are a hash wrapped in an array.
For reference
I did mention this issue here: https://github.com/rspec/rspec-mocks/issues/1306#issuecomment-756079746
but falsely thought it was an issue with .as_null_object
(I used fake_maker = double('Maker').as_null_object
instead of let(:maker) { Maker.new }
originally).
code https://github.com/rspec/rspec-mocks/blob/v3.10.1/lib/rspec/mocks/syntax.rb#L114