-
-
Notifications
You must be signed in to change notification settings - Fork 358
3.10.3 breaks hash argument matching in Ruby 3+ #1460
Comments
Thanks for reporting. Do you want to give this patch a go to see if it resolves the problem? |
The patch does not fix the issue, but it does at least provide better failure output, eg:
|
Something changed in the rspec-mocks gem between versions 3.10.2 and 3.10.3, because that's what causes tests to fail like this on Ruby 3.1.0 and 3.0.3 locally and on the CI: ``` Failure/Error: monitor_transaction(name, env, &block) Appsignal received :monitor_transaction with unexpected arguments expected: ("perform_job.something", {:key=>:value}) got: ("perform_job.something", {:key=>:value}) Diff: ``` In this example the arguments are exactly the same, except they're not the identical objects. This particular issue seems to be caused by a recent change that for Ruby 3 keyword argument matching: rspec/rspec-mocks#1460 ## Rewriting specs What helps is rewriting the tests to not mock method calls and check the arguments given. Instead, assert what's being stored on the transactions, like how we want all tests to be written as described in issue #252. ## Adding curly brackets For other specs that are a little more difficult to rewrite this way right now, I've chosen to add additional curly brackets around the argument expectations so that it's clear to Ruby/RSpec it's a hash argument and not keywords arguments. To solve this more permanently, we should rewrite the specs at some point. We can remove these curly brackets if the issue is fixed in a future rspec-mocks version as well. [skip changeset]
What this issue is reporting is actually the correct behaviour. On Ruby 3: expect(object).to receive(:foo).with('arg', opt: 'value') This is setting an expectation of calling class TestObject
def initialize(object)
@object = object
end
def foo(arg, options={})
@object.foo(arg, options)
end
end
class RealObject
def foo(arg, opt:)
end
end
TestObject.new(RealObject.new).foo('arg', opt: 'a')
# wrong number of arguments (given 2, expected 1; required keyword: opt) (ArgumentError) In the naive sense, e.g. without verifying doubles, there is simply no way to do any other behaviour, we must fail a spec for not matching keywords vs hashes, as the default behaviour on Ruby3 matches this, so your example is unsolvable. Now I expect prehaps in reality you are mocking a collaborator which has a defined behaviour, and indeed with instance doubles we could solve that scenario, where we have a real class we can see if the real object expects keywords or a hash, but we'd have to extend this existing behaviour in that scenario. |
I agree this is generally correct Ruby 3 behavior - a proper hash is not the same as keyword args. My issue is that a patch version change introduced behavior that makes matchers less permissive and breaks existing, working code. In your
|
The problem is a generic Previously keyword arguments were broken, and this change fixed them. That it unforuntately makes some previous specs invalid (which were incorrect on Ruby 3) is sad but there is no way for a generic double to fix it. I'd happily review a PR to make instance verifying doubles work (.e.g. The simple work around is to set |
The |
Something changed in the rspec-mocks gem between versions 3.10.2 and 3.10.3, because that's what causes tests to fail like this on Ruby 3.1.0 and 3.0.3 locally and on the CI: ``` Failure/Error: monitor_transaction(name, env, &block) Appsignal received :monitor_transaction with unexpected arguments expected: ("perform_job.something", {:key=>:value}) got: ("perform_job.something", {:key=>:value}) Diff: ``` In this example the arguments are exactly the same, except they're not the identical objects. This particular issue seems to be caused by a recent change that for Ruby 3 keyword argument matching: rspec/rspec-mocks#1460 ## Rewriting specs What helps is rewriting the tests to not mock method calls and check the arguments given. Instead, assert what's being stored on the transactions, like how we want all tests to be written as described in issue #252. ## Adding curly brackets For other specs that are a little more difficult to rewrite this way right now, I've chosen to add additional curly brackets around the argument expectations so that it's clear to Ruby/RSpec it's a hash argument and not keywords arguments. To solve this more permanently, we should rewrite the specs at some point. We can remove these curly brackets if the issue is fixed in a future rspec-mocks version as well. [skip changeset]
Previously keyword arguments were broken, and this change fixed them. That it unforuntately makes some previous specs invalid (which were incorrect on Ruby 3) is sad.
rspec-mocks 3.10.3 changed "with" syntax behavior to support ruby 3 keyword arguments separation: rspec/rspec-mocks#1394 rspec/rspec-mocks#1460 Fix yard testsuite accordingly. Fixes lsegal#1432
Previously the test failed on ruby 3.0.3 and rspec 3.10.3 due to expecting a method to be called with keyword arguments, but getting called with a hash. The issue is described in [1], but the problem stems from using a "double" since rspec has no way of knowing whether the "real" object accepts keyword args or a hash. This commit avoids the issue entirely by using a real object. [1] rspec/rspec-mocks#1460
We're seeing a similar issue(?) after upgrading rspec-rails, which updated rspec-mocks as a dependency... Failure/Error: update_async(user.id, properties)
ClassName received :method with unexpected arguments
expected: (5, {:subscription_started_date=>1646810075})
got: (5, {:subscription_started_date=>1646810075}) |
…n argument matching, cf. rspec/rspec-mocks#1460
We additionally pin rspec-mocks to 3.10.2 because 3.10.3 has a breaking change where keyword and hash behaviour is different. rspec/rspec-mocks#1460 We should probably change the tests to handle the Ruby 3 native format, but a working Ruby 3 instance is probably useful to developing that.
We have this issue during upgrade to ruby 3, the diff is misleading. In our case the hash being passed needed double splatting.
Fix:
|
Do you mind sharing your spec and the subject under test?
… On 25 Aug 2022, at 14:06, Rob Aldred ***@***.***> wrote:
We have this issue during upgrade to ruby 3, the diff is misleading.
#<Double "check"> received :create with unexpected arguments
expected: ("1111111-123123-123123", {:reports=>[{:name=>"identity"}, {:name=>"document"}], :tags=>["USA"], :type=>"express"})
got: ("1111111-123123-123123", {:reports=>[{:name=>"identity"}, {:name=>"document"}], :tags=>["USA"], :type=>"express"})
Fixed by changing the code calling this method to double splat the hash being passed
@api.check.create(applicant_id, **create_check_params(country_code: country_code))
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you commented.
|
* Update ruby to (3.1.2 -> 3.2.0) and bundler (2.3.7 -> 2.4.3) * Leave rubocop TargetRubyVersion at 3.1.2, because upgrade is hard * Use new shiny nokogiri version 1.14.0 * Update rspec-mocks to fix rspec/rspec-mocks#1460 * Fix specs since exception message now contains class name
* Déplace la gestion du droit d'admin de territoire * Update spec/requests/admin/territories/edit_territory_spec.rb Co-authored-by: François Ferrandis <francois.ferrandis@beta.gouv.fr> * Update app/views/admin/territories/agents/edit.html.slim Co-authored-by: François Ferrandis <francois.ferrandis@beta.gouv.fr> * Ruby 3.2.0 (#3253) * Update ruby to (3.1.2 -> 3.2.0) and bundler (2.3.7 -> 2.4.3) * Leave rubocop TargetRubyVersion at 3.1.2, because upgrade is hard * Use new shiny nokogiri version 1.14.0 * Update rspec-mocks to fix rspec/rspec-mocks#1460 * Fix specs since exception message now contains class name * Update spec/features/agents/admin_can_configure_the_territory_spec.rb Co-authored-by: François Ferrandis <francois.ferrandis@beta.gouv.fr> * Update spec/models/concerns/can_have_territorial_access_spec.rb Co-authored-by: François Ferrandis <francois.ferrandis@beta.gouv.fr> * Update spec/features/agents/admin_can_configure_the_territory_spec.rb Co-authored-by: François Ferrandis <francois.ferrandis@beta.gouv.fr> * Update spec/models/concerns/can_have_territorial_access_spec.rb Co-authored-by: François Ferrandis <francois.ferrandis@beta.gouv.fr> * Retire un doublon Co-authored-by: François Ferrandis <francois.ferrandis@beta.gouv.fr>
Passing hash and keyword values as function parameters in tests fail when using rspec-mocks >= v3.10.3. See rspec/rspec-mocks#1460 for more information.
Passing hash and keyword values as function parameters in tests fail when using rspec-mocks >= v3.10.3. See rspec/rspec-mocks#1460 for more information.
Running the tests at this point generates the following errors: expected: ({:body=>[{:index=>{:_id=>"/cheese", :_type=>"help_page"}}, {:link=>"/cheese", :title=>"We love cheese"}], :index=>"govuk_test"}) (keyword arguments) got: ({:body=>[{:index=>{:_id=>"/cheese", :_type=>"help_page"}}, {:link=>"/cheese", :title=>"We love cheese"}], :index=>"govuk_test"}) (options hash) expected: ({:body=>[{:delete=>{:_id=>"/cheese", :_type=>"help_page"}}], :index=>"govuk_test"}) (keyword arguments) got: ({:body=>[{:delete=>{:_id=>"/cheese", :_type=>"help_page"}}], :index=>"govuk_test"}) (options hash) These are caused by passing hash and keyword values as function parameters in tests when using rspec-mocks >= v3.10.3. See rspec/rspec-mocks#1460 and [here](alphagov/support-api@506bfe3) for more information.
See link for more details: rspec/rspec-mocks#1460
See link for more details: rspec/rspec-mocks#1460
RSpec tests under Ruby 3 were broken by ambiguous arguments. This patch fixes the test suite under Ruby 3 and is backward compatible. Discussion on this issue can be found in rspec repo: rspec/rspec-mocks#1460
RSpec tests under Ruby 3 were broken by ambiguous arguments. This patch fixes the test suite under Ruby 3 and is backward compatible. Discussion on this issue can be found in rspec repo: rspec/rspec-mocks#1460 Signed-off-by: Andrew Bobulsky <rulerof@gmail.com>
We additionally pin rspec-mocks to 3.10.2 because 3.10.3 has a breaking change where keyword and hash behaviour is different. rspec/rspec-mocks#1460 We should probably change the tests to handle the Ruby 3 native format, but a working Ruby 3 instance is probably useful to developing that.
See link for more details: rspec/rspec-mocks#1460
* fix(tests): Ruby 3 compatibility for RSpec suite RSpec tests under Ruby 3 were broken by ambiguous arguments. This patch fixes the test suite under Ruby 3 and is backward compatible. Discussion on this issue can be found in rspec repo: rspec/rspec-mocks#1460 Signed-off-by: Andrew Bobulsky <rulerof@gmail.com> * Minor chefstyle fixes Signed-off-by: Tim Smith <tsmith84@gmail.com> --------- Signed-off-by: Andrew Bobulsky <rulerof@gmail.com> Signed-off-by: Tim Smith <tsmith84@gmail.com> Co-authored-by: Tim Smith <tsmith@chef.io> Co-authored-by: Tim Smith <tim@mondoo.com>
body: Ruby 3.x keyword arguments (“kwargs”) are a first-class concept in Ruby 3.x onwards thus need to make it clear to Ruby/Rspec that arg is an actual hash not keyword argument. footer: - See [rspec/rspec-mocks#1460](rspec/rspec-mocks#1460)
Subject of the issue
With rspec-mocks 3.10.3 we started seeing test failures in our CI: Previously passing run - fails now with rspec mocks 3.10.3 for ruby 3+. (Note, passes with rspec-mocks 3.10.2).
I believe the change in behavior is related to #1394.
Note: This issue is similar to #1457 but applies to a slightly different case
Your environment
Steps to reproduce
The above tests can be fixed by either:
with('arg', {opt: 'value'})
@object.foo(arg, **options)
Expected behavior
Tests that pass in 3.10.2 should pass in 3.10.3.
Actual behavior
Some tests fail.
The text was updated successfully, but these errors were encountered: