Open vibro opened 1 year ago
I have a similar issue when mocking a File.read for a test. The 'allow' is acting like an 'expect' and preventing all other uses of File.read that are not specifically mocked.
allow(File).to receive(:read).with('myfile').and_return('my file contents')
Then prevents all other uses of File.read. I can work around this by first including
allow(File).to receive(:read).and_call_original
This seems like an unrelated issue @btalbot .Can you please open a separate issue providing a reproduction example?
So are you saying that if you File.read('someotherfile')
in your spec, it would also return 'my file contents'
?
@vibro Thanks for reporting.
started failing with the new version of rspec-mocks released
What was the previous version of rspec-mocks
that worked as expected for you?
I have a similar issue. Version 3.11.1 works without errors
@vibro At a glance, I could find #1394. Could you read comments there to see if there's an explanation to this behavioural change.
@rus-max What is the Ruby version that you're using? Can you provide an example of your code? I don't understand if "similar" refers to kwargs or allow...to receive...with
with positional arguments.
@pirj
ruby 3.1.2
allow(instance_double_class).to receive(:post).with(instance_of(MyClass), **params).and_return(stubbed_params)
Failure/Error:
#<InstanceDouble(InstanceDoubleClass) (anonymous)> received :post with unexpected arguments
expected: (an_instance_of(MyClass), {:id=>0, :method=>"method", :params=>{:param1=>"param1"}})
got: (#<Myclass foo="foo", bar="bar">, {:id=>0, :method=>"method", :params=>{:param1=>"param1"}})
Diff:
@@ -1,4 +1,4 @@
-["an_instance_of(MyClass)",
+[#<MyClass foo="foo", bar="bar">,
{:id=>0,
:method=>"method",
:params=>{:param1=>"param1"}}]
Please stub a default value first if message might be received with other args as well.
Make sure you are not passing hashes as kwargs, this is not valid on Ruby 3 and is now detected correctly (but the diff doesn't show the delta correctly on 3.11, that will come in a minor version bump)
@rus-max I suggest you to check #1473 for some information regarding instance_double(...)
changes.
What was the previous version of
rspec-mocks
that worked as expected for you?
3.11.1 works just fine.
I can update the code/change the behavior but this isn't something I would expect to change/break in a patch release.
Our support for keyword arguments isn't as good as it could be, a lot of usage such as yours has historically just worked due to implementation details of Ruby (mainly that keyword arguments carried over in splats and thus allowed usage in method calls and blocks transparently) with a bunch of edge case uses not being tested due to maintainers (including myself) not invisioning how they were used.
In Ruby 3 that changed and theres has had to be changes (which are mostly investigating and declaring ruby2_keywords correctly) to allow proper keyword arguments to be verified and treated differently to hashes (as in Ruby 3 you cannot mix splats).
In this case it appears that Net::SSH.start
does not take keyword arguments, it takes three positonal arguments (from https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh.rb#L216):
def self.start(host, user = nil, options = {}, &block)
So its non standard to be passing keyword arguments to via **kwargs
to this and is evidently tripping up some of our keyword detection, I won't revert that change, but as this seems to work on a standard method in Ruby 3.1:
def a_method(a, b, hash = {})
puts hash.inspect
end
a_method(1, 2, **{is_this_a_hash: :or_is_this_just_fantasty})
I would accept a PR helping to improve that detection.
Oddly I can't replicate this within our test suite on either 2.7.4
, 2.7.5
or 3.1.0
Edit: Removed output showing the snippet didn't work, it needed this adding for reproduction:
RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true # changing this to false makes the issue go away
end
end
An even futher odditiy is it seems to be specifically the Hash.new
that is causing the issue, a non empty hash does not trigger the snippet.
I can confirm we have this in our spec helper:
config.mock_with :rspec do |mocks|
# Prevents you from mocking or stubbing a method that does not exist on
# a real object. This is generally recommended, and will default to
# `true` in RSpec 4.
mocks.verify_partial_doubles = true
end
An even futher odditiy is it seems to be specifically the Hash.new that is causing the issue, a non empty hash does not trigger the snippet.
delegate({}) # => [{}] in Ruby 2.6 & 3, but [] in Ruby 2.7! (with a warning)
See the famous blog post.
In Ruby 2.7:
def foo(*args)
puts args.inspect
end
foo(**{}) # outputs `[]`
with
is defined with (*args)
.
This is the cause that
have_received(:start).with(host, user, **kwargs)
it the same as
have_received(:start).with(host, user)
But since the original method is defined with options = {}
, and calling it with **{}
sets options to its default {}
.
According to git bisect
it's not #1394 that breaks this case, but #1473
I've debugged this a bit, and it seems that the {}
part of args
was disappearing here.
Adding ruby2_keywords :proxy_method_invoked
fixes the snippet from the description. 🎉
This has already been fixed here.
But something else was fixed along the way, and the failure on main
is now quite the opposite:
# 3.11.2
Net::SSH received :start with unexpected arguments
expected: ("host.com", "party_parrot", {})
got: ("host.com", "party_parrot")
#main
(Net::SSH).start("host.com", "party_parrot")
expected: 1 time with arguments: ("host.com", "party_parrot")
received: 0 times
Version 3.11.2 issue with kwargs
Today our CI started failing with the new version of rspec-mocks released. It seems that mocking the kwargs no longer has the same behavior
Your environment
Steps to reproduce
I've provided a sample that is close to the behavior in our tests:
Expected behavior
Previously, this test ran ok.
Actual behavior
Now, we get a mocking error: