Permalink
Browse files

Added support for using RSpec's argument matchers with Not A Mock's A…

…rgsMatcher.

Extended RSpec's argument matchers with an in_any_order matcher, to match array arguments without caring about order of elements in the array. This is particularly useful for matching against arrays of database records, where you're not sure of the order in which the records have come out of the database.
  • Loading branch information...
1 parent 04999d1 commit 820c45647b99e4956831cd6099904681b458f696 @notahat committed Aug 19, 2008
View
@@ -1,4 +1,5 @@
require 'not_a_mock/active_record_extensions'
+require 'not_a_mock/argument_constraint_extensions'
require 'not_a_mock/call_recorder'
require 'not_a_mock/matchers'
require 'not_a_mock/matchers/anything_matcher'
@@ -0,0 +1,33 @@
+require 'set'
+
+module Spec #:nodoc:
+ module Mocks #:nodoc:
+
+ class AnyArgConstraint #:nodoc:
+ def inspect
+ 'anything'
+ end
+ end
+
+ class AnyOrderArgConstraint #:nodoc:
+ def initialize(array)
+ @array = array
+ end
+
+ def ==(arg)
+ Set.new(@array) == Set.new(arg)
+ end
+
+ def inspect
+ "in_any_order(#{@array.inspect})"
+ end
+ end
+
+ module ArgumentConstraintMatchers #:nodoc:
+ def in_any_order(array)
+ Spec::Mocks::AnyOrderArgConstraint.new(array)
+ end
+ end
+
+ end
+end
@@ -20,6 +20,8 @@ module NotAMock
#
# object.should have_received(:message).with(arg1, arg2, ...)
# object.should have_received(:message).without_args
+ #
+ # See NotAMock::Matchers::ArgsMatcher for more information.
#
# == Return Value Assertions
#
@@ -4,6 +4,43 @@ module NotAMock
module Matchers
# Matcher for
# with(...)
+ #
+ # == Argument Matchers
+ #
+ # Not A Mock supports the use of RSpec's patterns for argument matching
+ # in mocks, and extends them. The most useful are listed below.
+ #
+ # === Anything Matcher
+ #
+ # The +anything+ pattern will match any value. For example:
+ #
+ # object.should have_received(:message).with(1, anything, 3)
+ #
+ # will match the following calls:
+ #
+ # object.message(1, 2, 3)
+ # object.message(1, 'Boo!', 3)
+ #
+ # but not:
+ #
+ # object.message(3, 2, 1)
+ # object.message(1, 2, 3, 4)
+ #
+ # === In Any Order Matcher
+ #
+ # The +in_any_order+ pattern will match an array argument, but won't care
+ # about order of elements in the array. For example:
+ #
+ # object.should have_received(:message).with(in_any_order([3, 2, 1]))
+ #
+ # will match the following calls:
+ #
+ # object.message([3, 2, 1])
+ # object.message([1, 2, 3])
+ #
+ # but not:
+ #
+ # object.message([1, 2, 3, 4])
class ArgsMatcher < CallMatcher
def initialize(args, parent = nil)
@@ -12,7 +49,7 @@ def initialize(args, parent = nil)
end
def matches_without_parents?
- @calls = @parent.calls.select {|entry| entry[:args] == @args }
+ @calls = @parent.calls.select {|entry| @args == entry[:args] }
!@calls.empty?
end
View
@@ -57,31 +57,45 @@ def inspect
end
-describe NotAMock::Matchers::ArgsMatcher, " matching calls with arguments " do
+describe NotAMock::Matchers::ArgsMatcher, "matching calls with arguments " do
before do
- @object = TrackedClass.new({ :method => :length, :args => [1, 2, 3], :result => nil })
+ @object = TrackedClass.new({ :method => :length, :args => [1, [2, 3, 4], 5], :result => nil })
end
it "should match a called method with the correct arguments" do
- @matcher = have_received(:length).with(1, 2, 3)
+ @matcher = have_received(:length).with(1, [2, 3, 4], 5)
@matcher.matches?(@object).should be_true
- @matcher.negative_failure_message.should == "TrackedClass received length, with args [1, 2, 3]"
+ @matcher.negative_failure_message.should == "TrackedClass received length, with args [1, [2, 3, 4], 5]"
end
it "should not match a called method with the wrong arguments" do
@matcher = have_received(:length).with(3, 2, 1)
@matcher.matches?(@object).should be_false
@matcher.failure_message.should == "TrackedClass received length, but not with args [3, 2, 1]"
end
-
+
+ it "should match a called method with a wildcard argument" do
+ @matcher = have_received(:length).with(1, anything, 5)
+ @matcher.matches?(@object).should be_true
+ @matcher.matches?(@object).should be_true
+ @matcher.negative_failure_message.should == "TrackedClass received length, with args [1, anything, 5]"
+ end
+
+ it "should match a called method with a wildcard argument" do
+ @matcher = have_received(:length).with(1, in_any_order([4, 3, 2]), 5)
+ @matcher.matches?(@object).should be_true
+ @matcher.matches?(@object).should be_true
+ @matcher.negative_failure_message.should == "TrackedClass received length, with args [1, in_any_order([4, 3, 2]), 5]"
+ end
+
after do
NotAMock::CallRecorder.instance.reset
end
end
-describe NotAMock::Matchers::ArgsMatcher, " matching calls without arguments" do
+describe NotAMock::Matchers::ArgsMatcher, "matching calls without arguments" do
before do
@object = TrackedClass.new(

0 comments on commit 820c456

Please sign in to comment.