Skip to content

Commit

Permalink
Display keyword hashes in in expectation error messages
Browse files Browse the repository at this point in the history
Ref: vcr/vcr#925
Ref: #1394

I spent quite a lot of time figuring this error:

```
  2) VCR.turned_on passes options through to .turn_off!
     Failure/Error: turn_off!(options)

       VCR received :turn_off! with unexpected arguments
         expected: ({:ignore_cassettes=>true})
              got: ({:ignore_cassettes=>true})
     # ./lib/vcr.rb:317:in `turned_on'
     # ./spec/lib/vcr_spec.rb:367:in `block (3 levels) in <top (required)>'
```

I quickly suspected it was a keyword argument issue, but it's far from
obvious to everyone, and even when you are familair with the issue
it doesn't tell you what was expected and what was received.

I doubt the way I implemented this is ok, but I think it's worth
opening the discussion

```
  2) VCR.turned_on passes options through to .turn_off!
     Failure/Error: turn_off!(options)

       VCR received :turn_off! with unexpected arguments
         expected: ({:ignore_cassettes=>true}) (keyword arguments)
              got: ({:ignore_cassettes=>true}) (options hash)
     # ./lib/vcr.rb:317:in `turned_on'
     # ./spec/lib/vcr_spec.rb:367:in `block (3 levels) in <top (required)>'
```
  • Loading branch information
byroot authored and pirj committed Mar 22, 2022
1 parent 7823b0b commit e931e81
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 0 deletions.
11 changes: 11 additions & 0 deletions lib/rspec/mocks/error_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,17 @@ def unexpected_arguments_message(expected_args_string, actual_args_string)
def error_message(expectation, args_for_multiple_calls)
expected_args = format_args(expectation.expected_args)
actual_args = format_received_args(args_for_multiple_calls)

if RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash? && expected_args == actual_args
expected_hash = expectation.expected_args.last
actual_hash = args_for_multiple_calls.last.last
if Hash === expected_hash && Hash === actual_hash &&
(Hash.ruby2_keywords_hash?(expected_hash) != Hash.ruby2_keywords_hash?(actual_hash))
actual_args += Hash.ruby2_keywords_hash?(actual_hash) ? " (keyword arguments)" : " (options hash)"
expected_args += Hash.ruby2_keywords_hash?(expected_hash) ? " (keyword arguments)" : " (options hash)"
end
end

message = default_error_message(expectation, expected_args, actual_args)

if args_for_multiple_calls.one?
Expand Down
33 changes: 33 additions & 0 deletions spec/rspec/mocks/diffing_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,39 @@
end
end

if RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash?
eval <<-'RUBY', nil, __FILE__, __LINE__ + 1
it "print a diff when keyword argument were expected but got an option hash (using splat)" do
with_unfulfilled_double do |d|
expect(d).to receive(:foo).with(**expected_hash)
expect {
d.foo(expected_hash)
}.to fail_with(
"#<Double \"double\"> received :foo with unexpected arguments\n" \
" expected: ({:baz=>:quz, :foo=>:bar}) (keyword arguments)\n" \
" got: ({:baz=>:quz, :foo=>:bar}) (options hash)"
)
end
end
RUBY

eval <<-'RUBY', nil, __FILE__, __LINE__ + 1
it "print a diff when keyword argument were expected but got an option hash (literal)" do
with_unfulfilled_double do |d|
expect(d).to receive(:foo).with(:positional, keyword: 1)
expect {
options = { keyword: 1 }
d.foo(:positional, options)
}.to fail_with(
"#<Double \"double\"> received :foo with unexpected arguments\n" \
" expected: (:positional, {:keyword=>1}) (keyword arguments)\n" \
" got: (:positional, {:keyword=>1}) (options hash)"
)
end
end
RUBY
end

if RUBY_VERSION.to_f < 1.9
# Ruby 1.8 hashes are not ordered, but `#inspect` on a particular unchanged
# hash instance should return consistent output. However, on Travis that does
Expand Down

0 comments on commit e931e81

Please sign in to comment.