Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/rspec/mocks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class << self

# Namespace for mock-related matchers.
module Matchers
autoload :Matcher, "rspec/mocks/matchers/matcher"
autoload :HaveReceived, "rspec/mocks/matchers/have_received"
autoload :Receive, "rspec/mocks/matchers/receive"
autoload :ReceiveMessageChain, "rspec/mocks/matchers/receive_message_chain"
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/mocks/matchers/have_received.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module Mocks
module Matchers
# @private
class HaveReceived
include Matcher

COUNT_CONSTRAINTS = %w[exactly at_least at_most times once twice thrice]
ARGS_CONSTRAINTS = %w[with]
CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w[ordered]
Expand Down
8 changes: 8 additions & 0 deletions lib/rspec/mocks/matchers/matcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module RSpec
module Mocks
module Matchers
# just a "tag" for rspec-mock matchers detection
module Matcher; end
end
end
end
2 changes: 2 additions & 0 deletions lib/rspec/mocks/matchers/receive.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module Mocks
module Matchers
# @private
class Receive
include Matcher

def initialize(message, block)
@message = message
@block = block
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/mocks/matchers/receive_message_chain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module Mocks
module Matchers
# @private
class ReceiveMessageChain
include Matcher

def initialize(chain, &block)
@chain = chain
@block = block
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/mocks/matchers/receive_messages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module Mocks
module Matchers
# @private
class ReceiveMessages
include Matcher

def initialize(message_return_value_hash)
@message_return_value_hash = message_return_value_hash
@backtrace_line = CallerFilter.first_non_rspec_line
Expand Down
2 changes: 1 addition & 1 deletion lib/rspec/mocks/targets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def self.disallow_negation(method_name)
private

def matcher_allowed?(matcher)
matcher.class.name.start_with?("RSpec::Mocks::Matchers".freeze)
Matchers::Matcher === matcher
end

def define_matcher(matcher, name, &block)
Expand Down
6 changes: 6 additions & 0 deletions spec/rspec/mocks/matchers/receive_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ module Mocks
}.to raise_error(UnsupportedMatcherError)
end

it 'does support inherited matchers', :unless => options.include?(:allow_other_matchers) do
receive_foo = Class.new(RSpec::Mocks::Matchers::Receive).new(:foo, nil)
wrapped.to receive_foo
receiver.foo
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a shared spec that is meant to run in 4 different contexts:

  • expect(object).to receive_foo
  • allow(object).to receive_foo
  • expect_any_instance_of(object).to receive_foo
  • allow_any_instance_of(object).to receive_foo

But as you've written it, it tests just the first 2 of these, and does so for all 4 contexts this shared spec is run in. Instead, I think it should be:

        it 'supports inherited matchers' do
          receive_foo = Class.new(RSpec::Mocks::Matchers::Receive).new(:foo, nil)
          wrapped.to receive_foo
          receiver.foo
        end

wrapped is polymorphic and will return allow(object), expect(object) etc. receiver will return object or an instance of the class passed to allow_any_instance_of(klass)/expect_any_instance_of(klass) -- so by using these, you can write one spec and have it operate for all the contexts appropriately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I got it, thanks!


it 'does not get confused by messages being passed as strings and symbols' do
wrapped.to receive(:foo).with(1) { :a }
wrapped.to receive("foo").with(2) { :b }
Expand Down