module Spec module Mocks # ArgumentMatchers are messages that you can include in message # expectations to match arguments against a broader check than simple # equality. # # With the exception of any_args() and no_args(), the matchers # are all positional - they match against the arg in the given position. module ArgumentMatchers class AnyArgsMatcher def description "any args" end end class NoArgsMatcher def description "no args" end end class AnyArgMatcher def initialize(ignore) end def ==(other) true end def description "anything" end end class RegexpMatcher def initialize(regexp) @regexp = regexp end def ==(value) return value =~ @regexp unless value.is_a?(Regexp) value == @regexp end end class BooleanMatcher def initialize(ignore) end def ==(value) TrueClass === value || FalseClass === value end end class HashIncludingMatcher def initialize(expected) @expected = expected end def ==(actual) @expected.each do | key, value | return false unless actual.has_key?(key) && value == actual[key] end true rescue NoMethodError => ex return false end def description "hash_including(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})" end end class HashNotIncludingMatcher def initialize(expected) @expected = expected end def ==(actual) @expected.each do | key, value | return false if actual.has_key?(key) && value == actual[key] end true rescue NoMethodError => ex return false end def description "hash_not_including(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})" end end class DuckTypeMatcher def initialize(*methods_to_respond_to) @methods_to_respond_to = methods_to_respond_to end def ==(value) @methods_to_respond_to.all? { |sym| value.respond_to?(sym) } end end class MatcherMatcher def initialize(matcher) @matcher = matcher end def ==(value) @matcher.matches?(value) end end class EqualityProxy def initialize(given) @given = given end def ==(expected) @given == expected end end class InstanceOf def initialize(klass) @klass = klass end def ==(actual) actual.instance_of?(@klass) end end class KindOf def initialize(klass) @klass = klass end def ==(actual) actual.kind_of?(@klass) end end # :call-seq: # object.should_receive(:message).with(no_args()) # # Passes if no arguments are passed along with the message def no_args NoArgsMatcher.new end # :call-seq: # object.should_receive(:message).with(any_args()) # # Passes if object receives :message with any args at all. This is # really a more explicit variation of object.should_receive(:message) def any_args AnyArgsMatcher.new end # :call-seq: # object.should_receive(:message).with(anything()) # # Passes as long as there is an argument. def anything AnyArgMatcher.new(nil) end # :call-seq: # object.should_receive(:message).with(duck_type(:hello)) # object.should_receive(:message).with(duck_type(:hello, :goodbye)) # # Passes if the argument responds to the specified messages. # # == Examples # # array = [] # display = mock('display') # display.should_receive(:present_names).with(duck_type(:length, :each)) # => passes def duck_type(*args) DuckTypeMatcher.new(*args) end # :call-seq: # object.should_receive(:message).with(boolean()) # # Passes if the argument is boolean. def boolean BooleanMatcher.new(nil) end # :call-seq: # object.should_receive(:message).with(hash_including(:key => val)) # object.should_receive(:message).with(hash_including(:key)) # object.should_receive(:message).with(hash_including(:key, :key2 => val2)) # Passes if the argument is a hash that includes the specified key(s) or key/value # pairs. If the hash includes other keys, it will still pass. def hash_including(*args) HashIncludingMatcher.new(anythingize_lonely_keys(*args)) end # :call-seq: # object.should_receive(:message).with(hash_not_including(:key => val)) # object.should_receive(:message).with(hash_not_including(:key)) # object.should_receive(:message).with(hash_not_including(:key, :key2 => :val2)) # # Passes if the argument is a hash that doesn't include the specified key(s) or key/value def hash_not_including(*args) HashNotIncludingMatcher.new(anythingize_lonely_keys(*args)) end # Passes if arg.instance_of?(klass) def instance_of(klass) InstanceOf.new(klass) end alias_method :an_instance_of, :instance_of # Passes if arg.kind_of?(klass) def kind_of(klass) KindOf.new(klass) end alias_method :a_kind_of, :kind_of private def anythingize_lonely_keys(*args) hash = args.last.class == Hash ? args.delete_at(-1) : {} args.each { | arg | hash[arg] = anything } hash end end end end