Skip to content

Commit

Permalink
Merge pull request #301 from samphippen/array-including-matcher
Browse files Browse the repository at this point in the history
Add an array_including argument matcher
  • Loading branch information
Sam Phippen committed May 30, 2013
2 parents 8309e29 + 14da850 commit 2214e27
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 0 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Breaking Changes for 3.0.0:
Enhancements:

* Document test spies in the readme. (Adarsh Pandit)
* Add an `array_including` matcher. (Sam Phippen)

### 2.14.0.rc1 / 2013-05-27
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1)
Expand Down
3 changes: 3 additions & 0 deletions features/method_stubs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ You can also use the block format:
obj.stub(:message).with(anything()) { ... }
obj.stub(:message).with(an_instance_of(Money)) { ... }
obj.stub(:message).with(hash_including(:a => 'b')) { ... }
obj.stub(:message).with(array_including(1,2,3)) { ... }
# or
obj.stub(:message).with(array_including([1,2,3])) { ... }

#### Regular expressions

Expand Down
26 changes: 26 additions & 0 deletions lib/rspec/mocks/argument_matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,20 @@ def description
end
end

class ArrayIncludingMatcher
def initialize(expected)
@expected = expected
end

def ==(actual)
Set.new(actual).superset?(Set.new(@expected))
end

def description
"array_including(#{@expected.join(",")})"
end
end

class DuckTypeMatcher
def initialize(*methods_to_respond_to)
@methods_to_respond_to = methods_to_respond_to
Expand Down Expand Up @@ -192,6 +206,18 @@ def hash_including(*args)
HashIncludingMatcher.new(anythingize_lonely_keys(*args))
end

# Matches an array that includes the specified items at least once.
# Ignores duplicates and additional values
#
# @example
#
# object.should_receive(:message).with(array_including(1,2,3))
# object.should_receive(:message).with(hash_including([1,2,3]))
def array_including(*args)
actually_an_array = args.first.is_a?(Array) && args.count == 1 ? args.first : args
ArrayIncludingMatcher.new(actually_an_array)
end

# Matches a hash that doesn't include the specified key(s) or key/value.
#
# @example
Expand Down
41 changes: 41 additions & 0 deletions spec/rspec/mocks/array_including_matcher_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'spec_helper'

module RSpec
module Mocks
module ArgumentMatchers
describe ArrayIncludingMatcher do
it "describes itself properly" do
expect(ArrayIncludingMatcher.new([1, 2, 3]).description).to eq "array_including(1,2,3)"
end

context "passing" do
it "matches the same array" do
expect(array_including(1, 2, 3)).to eq([1, 2, 3])
end

it "matches the same array, specified without square brackets" do
expect(array_including(1, 2, 3)).to eq([1, 2, 3])
end

it "matches the same array, which includes nested arrays" do
expect(array_including([1, 2], 3, 4)).to eq([[1, 2], 3, 4])
end

it "works with duplicates in expected" do
expect(array_including(1, 1, 2, 3)).to eq([1, 2, 3])
end

it "works with duplicates in actual" do
expect(array_including(1, 2, 3)).to eq([1, 1, 2, 3])
end
end

context "failing" do
it "fails when not all the entries in the expected are present" do
expect(array_including(1,2,3,4,5)).not_to eq([1,2])
end
end
end
end
end
end
14 changes: 14 additions & 0 deletions spec/rspec/mocks/failing_argument_matchers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,20 @@ module Mocks
end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (hash_including(:a=>1))\n got: ({})")
end

it "fails array_including when args aren't array" do
expect do
@double.should_receive(:msg).with(array_including(1,2,3))
@double.msg(1,2,3)
end.to raise_error(/array_including\(1,2,3\)/)
end

it "fails array_including when arg doesn't contain all elements" do
expect do
@double.should_receive(:msg).with(array_including(1,2,3))
@double.msg(1,2)
end.to raise_error(/array_including\(1,2,3\)/)
end

it "fails with block matchers" do
expect do
@double.should_receive(:msg).with {|arg| expect(arg).to eq :received }
Expand Down
5 changes: 5 additions & 0 deletions spec/rspec/mocks/passing_argument_matchers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ module Mocks
@double.should_receive(:random_call).with(hash_including(:a => 1))
@double.random_call(:a => 1)
end

it "matches array with array_including same array" do
@double.should_receive(:random_call).with(array_including(1,2))
@double.random_call([1,2])
end
end

context "handling block matchers" do
Expand Down

0 comments on commit 2214e27

Please sign in to comment.