Skip to content

Commit

Permalink
[mocks] Merge pull request rspec/rspec-mocks#1394 from mame/with-for-…
Browse files Browse the repository at this point in the history
…ruby3

Make `with` support Ruby 3 keywords

---
This commit was imported from rspec/rspec-mocks@fefabc4.
  • Loading branch information
JonRowe authored Feb 18, 2021
2 parents b69c137 + 0bb408d commit ae7ed8a
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 44 deletions.
20 changes: 17 additions & 3 deletions rspec-mocks/lib/rspec/mocks/argument_list_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,30 @@ def initialize(*expected_args)
@expected_args = expected_args
ensure_expected_args_valid!
end
ruby2_keywords :initialize if Module.private_method_defined?(:ruby2_keywords)

# @api public
# @param [Array] args
# @param [Array] actual_args
#
# Matches each element in the `expected_args` against the element in the same
# position of the arguments passed to `new`.
#
# @see #initialize
def args_match?(*args)
Support::FuzzyMatcher.values_match?(resolve_expected_args_based_on(args), args)
def args_match?(*actual_args)
expected_args = resolve_expected_args_based_on(actual_args)

return false if expected_args.size != actual_args.size

if RUBY_VERSION >= "3"
# if both arguments end with Hashes, and if one is a keyword hash and the other is not, they don't match
if Hash === expected_args.last && Hash === actual_args.last
if !Hash.ruby2_keywords_hash?(actual_args.last) && Hash.ruby2_keywords_hash?(expected_args.last)
return false
end
end
end

Support::FuzzyMatcher.values_match?(expected_args, actual_args)
end

# @private
Expand Down
1 change: 1 addition & 0 deletions rspec-mocks/lib/rspec/mocks/matchers/receive.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def setup_any_instance_allowance(subject, &block)
@recorded_customizations << ExpectationCustomization.new(method, args, block)
self
end
ruby2_keywords(method) if Module.private_method_defined?(:ruby2_keywords)
end

private
Expand Down
1 change: 1 addition & 0 deletions rspec-mocks/lib/rspec/mocks/message_expectation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ def with(*args, &block)
@argument_list_matcher = ArgumentListMatcher.new(*args)
self
end
ruby2_keywords(:with) if Module.private_method_defined?(:ruby2_keywords)

# Expect messages to be received in a specific order.
#
Expand Down
20 changes: 15 additions & 5 deletions rspec-mocks/spec/rspec/mocks/argument_matchers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -381,16 +381,26 @@ def ==(other)
a_double.random_call(:a => "a", :b => "b")
end

it "matches against a hash submitted by reference and received by value" do
it "matches against a hash submitted as keyword arguments a and received as a positional argument (in both Ruby 2 and Ruby 3)" do
opts = {:a => "a", :b => "b"}
expect(a_double).to receive(:random_call).with(opts)
a_double.random_call(:a => "a", :b => "b")
end

it "matches against a hash submitted by value and received by reference" do
opts = {:a => "a", :b => "b"}
expect(a_double).to receive(:random_call).with(:a => "a", :b => "b")
a_double.random_call(opts)
if RUBY_VERSION >= "3"
it "fails to matches against a hash submitted as a positional argument and received as keyword arguments in Ruby 3.0 or later", :reset => true do
opts = {:a => "a", :b => "b"}
expect(a_double).to receive(:random_call).with(:a => "a", :b => "b")
expect do
a_double.random_call(opts)
end.to fail_with(/expected: \(\{(:a=>\"a\", :b=>\"b\"|:b=>\"b\", :a=>\"a\")\}\)/)
end
else
it "matches against a hash submitted as a positional argument and received as keyword arguments in Ruby 2.7 or before" do
opts = {:a => "a", :b => "b"}
expect(a_double).to receive(:random_call).with(:a => "a", :b => "b")
a_double.random_call(opts)
end
end

it "fails for a hash w/ wrong values", :reset => true do
Expand Down
13 changes: 9 additions & 4 deletions rspec-mocks/spec/rspec/mocks/partial_double_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,15 @@ def call(name)
expect(object.foobar(:key => "value")).to equal(1)
end

it "can accept an inner hash as a message argument" do
hash = {:a => {:key => "value"}}
expect(object).to receive(:foobar).with(:key => "value").and_return(1)
expect(object.foobar(hash[:a])).to equal(1)
if RSpec::Support::RubyFeatures.required_kw_args_supported?
# Use eval to avoid syntax error on 1.8 and 1.9
binding.eval(<<-CODE, __FILE__, __LINE__)
it "can accept an inner hash as a message argument" do
hash = {:a => {:key => "value"}}
expect(object).to receive(:foobar).with(:key => "value").and_return(1)
expect(object.foobar(**hash[:a])).to equal(1)
end
CODE
end

it "can create a positive message expectation" do
Expand Down
104 changes: 72 additions & 32 deletions rspec-mocks/spec/rspec/mocks/should_syntax_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -463,39 +463,79 @@ def use_rspec_mocks
after(:all) { RSpec::Mocks.configuration.syntax = orig_syntax }
before { RSpec::Mocks.configuration.reset_syntaxes_to_default }

let(:expected_arguments) {
[
/Using.*without explicitly enabling/,
if RSpec::Support::RubyFeatures.required_kw_args_supported?
let(:expected_arguments) {
[
/Using.*without explicitly enabling/,
]
}
let(:expected_keywords) {
{:replacement => "the new `:expect` syntax or explicitly enable `:should`"}
]
}

it "it warns about should once, regardless of how many times it is called" do
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
o = Object.new
o2 = Object.new
o.should_receive(:bees)
o2.should_receive(:bees)

o.bees
o2.bees
end

it "warns about should not once, regardless of how many times it is called" do
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
o = Object.new
o2 = Object.new
o.should_not_receive(:bees)
o2.should_not_receive(:bees)
end

it "warns about stubbing once, regardless of how many times it is called" do
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
o = Object.new
o2 = Object.new

o.stub(:faces)
o2.stub(:faces)
}
it "it warns about should once, regardless of how many times it is called" do
# Use eval to avoid syntax error on 1.8 and 1.9
eval("expect(RSpec).to receive(:deprecate).with(*expected_arguments, **expected_keywords)")
o = Object.new
o2 = Object.new
o.should_receive(:bees)
o2.should_receive(:bees)

o.bees
o2.bees
end

it "warns about should not once, regardless of how many times it is called" do
# Use eval to avoid syntax error on 1.8 and 1.9
eval("expect(RSpec).to receive(:deprecate).with(*expected_arguments, **expected_keywords)")
o = Object.new
o2 = Object.new
o.should_not_receive(:bees)
o2.should_not_receive(:bees)
end

it "warns about stubbing once, regardless of how many times it is called" do
# Use eval to avoid syntax error on 1.8 and 1.9
eval("expect(RSpec).to receive(:deprecate).with(*expected_arguments, **expected_keywords)")
o = Object.new
o2 = Object.new

o.stub(:faces)
o2.stub(:faces)
end
else
let(:expected_arguments) {
[
/Using.*without explicitly enabling/,
{:replacement => "the new `:expect` syntax or explicitly enable `:should`"}
]
}
it "it warns about should once, regardless of how many times it is called" do
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
o = Object.new
o2 = Object.new
o.should_receive(:bees)
o2.should_receive(:bees)

o.bees
o2.bees
end

it "warns about should not once, regardless of how many times it is called" do
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
o = Object.new
o2 = Object.new
o.should_not_receive(:bees)
o2.should_not_receive(:bees)
end

it "warns about stubbing once, regardless of how many times it is called" do
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
o = Object.new
o2 = Object.new

o.stub(:faces)
o2.stub(:faces)
end
end

it "warns about unstubbing once, regardless of how many times it is called" do
Expand Down

0 comments on commit ae7ed8a

Please sign in to comment.