Skip to content

Commit

Permalink
Provide a clear failure when invalid args are passed to with.
Browse files Browse the repository at this point in the history
  • Loading branch information
myronmarston committed Oct 4, 2014
1 parent 903ebe5 commit 80f6f62
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 5 deletions.
9 changes: 9 additions & 0 deletions Changelog.md
Expand Up @@ -20,6 +20,15 @@ Enhancements:
* Treat `any_args` as an arg splat, allowing it to match an arbitrary
number of args at any point in an arg list. (Myron Marston, #786)

Bug Fixes:

* Provide a clear error when users wrongly combine `no_args` with
additional arguments (e.g. `expect().to receive().with(no_args, 1)`).
(Myron Marston, #786)
* Provide a clear error when users wrongly use `any_args` multiple times in the
same argument list (e.g. `expect().to receive().with(any_args, 1, any_args)`.
(Myron Marston, #786)

### 3.1.1 / 2014-09-18
[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...v3.1.1)

Expand Down
22 changes: 17 additions & 5 deletions lib/rspec/mocks/argument_list_matcher.rb
Expand Up @@ -44,6 +44,7 @@ class ArgumentListMatcher
# @see #args_match?
def initialize(*expected_args)
@expected_args = expected_args
ensure_expected_args_valid!
end

# @api public
Expand All @@ -57,11 +58,6 @@ def args_match?(*args)
Support::FuzzyMatcher.values_match?(matchers_for(args), args)
end

# Value that will match all argument lists.
#
# @private
MATCH_ALL = new(ArgumentMatchers::AnyArgsMatcher::INSTANCE)

private

def matchers_for(actual_args)
Expand All @@ -80,6 +76,22 @@ def replace_any_args_with_splat_of_anything(before_count, actual_args_count)
any_args = 1.upto(any_args_count).map { ArgumentMatchers::AnyArgMatcher::INSTANCE }
expected_args.first(before_count) + any_args + expected_args.last(after_count)
end

def ensure_expected_args_valid!
if expected_args.count(ArgumentMatchers::AnyArgsMatcher::INSTANCE) > 1
raise ArgumentError, "`any_args` can only be passed to " \
"`with` once but you have passed it multiple times."
elsif expected_args.count > 1 && expected_args.include?(ArgumentMatchers::NoArgsMatcher::INSTANCE)
raise ArgumentError, "`no_args` can only be passed as a " \
"singleton argument to `with` (i.e. `with(no_args)`), " \
"but you have passed additional arguments."
end
end

# Value that will match all argument lists.
#
# @private
MATCH_ALL = new(ArgumentMatchers::AnyArgsMatcher::INSTANCE)
end
end
end
16 changes: 16 additions & 0 deletions spec/rspec/mocks/argument_matchers_spec.rb
Expand Up @@ -216,6 +216,14 @@ module Mocks
expect { a_double.random_call }.to fail_matching "expected: (1, *(any args), /foo/)"
end
end

context "when passed twice" do
it 'immediately signals that this is invalid', :reset => true do
expect {
expect(a_double).to receive(:random_call).with(any_args, 1, any_args)
}.to raise_error(ArgumentError, /any_args/)
end
end
end

describe "no_args" do
Expand All @@ -228,6 +236,14 @@ module Mocks
expect(a_double).to receive(:msg).with(no_args)
expect { a_double.msg(37) }.to fail_matching "expected: (no args)"
end

context "when passed with other arguments" do
it 'immediately signals that this is invalid', :reset => true do
expect {
expect(a_double).to receive(:random_call).with(no_args, 3)
}.to raise_error(ArgumentError, /no_args/)
end
end
end

describe "hash_including" do
Expand Down

0 comments on commit 80f6f62

Please sign in to comment.