From c3b567da4825084aa97386bf0160cd3e66389160 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 7 Oct 2019 12:19:29 +0100 Subject: [PATCH] Revert "Merge pull request #1125 from pirj/prevent-block-matchers-from-being-used-with-value-expectation-target" This reverts commit f5bf9d8264ffd786a66fe25c3bb1125c93b59d89. --- Changelog.md | 9 +- lib/rspec/expectations/expectation_target.rb | 50 ++------ lib/rspec/expectations/syntax.rb | 2 +- lib/rspec/matchers/built_in/base_matcher.rb | 5 - lib/rspec/matchers/built_in/change.rb | 25 ++-- lib/rspec/matchers/built_in/compound.rb | 14 --- lib/rspec/matchers/built_in/output.rb | 9 +- lib/rspec/matchers/built_in/raise_error.rb | 7 +- lib/rspec/matchers/built_in/throw_symbol.rb | 9 +- lib/rspec/matchers/built_in/yield.rb | 50 ++++---- lib/rspec/matchers/dsl.rb | 4 - lib/rspec/matchers/matcher_protocol.rb | 6 - .../expectations/extensions/kernel_spec.rb | 4 +- spec/rspec/matchers/aliased_matcher_spec.rb | 2 +- spec/rspec/matchers/built_in/all_spec.rb | 2 +- .../matchers/built_in/be_between_spec.rb | 2 +- .../matchers/built_in/be_instance_of_spec.rb | 2 +- .../matchers/built_in/be_kind_of_spec.rb | 2 +- .../rspec/matchers/built_in/be_within_spec.rb | 2 +- spec/rspec/matchers/built_in/change_spec.rb | 56 ++++----- spec/rspec/matchers/built_in/compound_spec.rb | 8 +- .../matchers/built_in/contain_exactly_spec.rb | 2 +- spec/rspec/matchers/built_in/cover_spec.rb | 2 +- spec/rspec/matchers/built_in/eq_spec.rb | 2 +- spec/rspec/matchers/built_in/eql_spec.rb | 2 +- spec/rspec/matchers/built_in/equal_spec.rb | 2 +- spec/rspec/matchers/built_in/exist_spec.rb | 4 +- spec/rspec/matchers/built_in/has_spec.rb | 4 +- .../matchers/built_in/have_attributes_spec.rb | 4 +- spec/rspec/matchers/built_in/include_spec.rb | 2 +- spec/rspec/matchers/built_in/match_spec.rb | 2 +- spec/rspec/matchers/built_in/output_spec.rb | 7 +- .../matchers/built_in/raise_error_spec.rb | 8 +- .../matchers/built_in/respond_to_spec.rb | 2 +- spec/rspec/matchers/built_in/satisfy_spec.rb | 2 +- .../built_in/start_and_end_with_spec.rb | 4 +- .../matchers/built_in/throw_symbol_spec.rb | 8 +- spec/rspec/matchers/built_in/yield_spec.rb | 57 ++------- .../matchers/define_negated_matcher_spec.rb | 2 +- spec/rspec/matchers/dsl_spec.rb | 2 +- spec/rspec/matchers_spec.rb | 5 +- spec/spec_helper.rb | 4 +- spec/support/shared_examples.rb | 111 ++++++++++++++++++ spec/support/shared_examples/block_matcher.rb | 80 ------------- spec/support/shared_examples/matcher.rb | 44 ------- spec/support/shared_examples/value_matcher.rb | 66 ----------- 46 files changed, 242 insertions(+), 456 deletions(-) create mode 100644 spec/support/shared_examples.rb delete mode 100644 spec/support/shared_examples/block_matcher.rb delete mode 100644 spec/support/shared_examples/matcher.rb delete mode 100644 spec/support/shared_examples/value_matcher.rb diff --git a/Changelog.md b/Changelog.md index ae3e629f3..460150777 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,13 @@ Enhancements: * Add a warning when an empty diff is produce due to identical inspect output. (Benoit Tigeot, #1126) +### 3.8.6 / 2019-10-07 + +Bug Fixes: + +* Revert #1125 due to the change being incompatible with our semantic versioning + policy. + ### 3.8.5 / 2019-10-02 [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.8.4...v3.8.5) @@ -36,7 +43,7 @@ Bug Fixes: * Prevent composed `all` matchers from leaking into their siblings leading to duplicate failures. (Jamie English, #1086) * Prevent objects which change their hash on comparison from failing change checks. - (Phil Pirozhkov, #1100) + (Phil Pirozhkov, #1110) * Issue an `ArgumentError` rather than a `NoMethodError` when `be_an_instance_of` and `be_kind_of` matchers encounter objects not supporting those methods. (Taichi Ishitani, #1107) diff --git a/lib/rspec/expectations/expectation_target.rb b/lib/rspec/expectations/expectation_target.rb index 77d17bc91..176b31224 100644 --- a/lib/rspec/expectations/expectation_target.rb +++ b/lib/rspec/expectations/expectation_target.rb @@ -33,16 +33,16 @@ def initialize(value) end # @private - def self.for(value, &block) + def self.for(value, block) if UndefinedValue.equal?(value) - unless block_given? + unless block raise ArgumentError, "You must pass either an argument or a block to `expect`." end BlockExpectationTarget.new(block) - elsif block_given? + elsif block raise ArgumentError, "You cannot pass both an argument and a block to `expect`." else - ValueExpectationTarget.new(value) + new(value) end end @@ -90,40 +90,6 @@ def prevent_operator_matchers(verb) include InstanceMethods end - # @private - # Validates the provided matcher to ensure it supports block - # expectations, in order to avoid user confusion when they - # use a block thinking the expectation will be on the return - # value of the block rather than the block itself. - class ValueExpectationTarget < ExpectationTarget - def to(matcher=nil, message=nil, &block) - enforce_value_expectation(matcher) - super - end - - def not_to(matcher=nil, message=nil, &block) - enforce_value_expectation(matcher) - super - end - - private - - def enforce_value_expectation(matcher) - return if supports_value_expectations?(matcher) - - raise ExpectationNotMetError, "You must pass a block rather than an argument to `expect` to use the provided " \ - "block expectation matcher (#{RSpec::Support::ObjectFormatter.format(matcher)})." - end - - def supports_value_expectations?(matcher) - if matcher.respond_to?(:supports_value_expectations?) - matcher.supports_value_expectations? - else - true - end - end - end - # @private # Validates the provided matcher to ensure it supports block # expectations, in order to avoid user confusion when they @@ -152,11 +118,9 @@ def enforce_block_expectation(matcher) end def supports_block_expectations?(matcher) - if matcher.respond_to?(:supports_block_expectations?) - matcher.supports_block_expectations? - else - false - end + matcher.supports_block_expectations? + rescue NoMethodError + false end end end diff --git a/lib/rspec/expectations/syntax.rb b/lib/rspec/expectations/syntax.rb index 3906d8520..b8430346f 100644 --- a/lib/rspec/expectations/syntax.rb +++ b/lib/rspec/expectations/syntax.rb @@ -70,7 +70,7 @@ def enable_expect(syntax_host=::RSpec::Matchers) syntax_host.module_exec do def expect(value=::RSpec::Expectations::ExpectationTarget::UndefinedValue, &block) - ::RSpec::Expectations::ExpectationTarget.for(value, &block) + ::RSpec::Expectations::ExpectationTarget.for(value, block) end end end diff --git a/lib/rspec/matchers/built_in/base_matcher.rb b/lib/rspec/matchers/built_in/base_matcher.rb index ec957be9d..7699c92ac 100644 --- a/lib/rspec/matchers/built_in/base_matcher.rb +++ b/lib/rspec/matchers/built_in/base_matcher.rb @@ -78,11 +78,6 @@ def supports_block_expectations? false end - # @private - def supports_value_expectations? - true - end - # @api private def expects_call_stack_jump? false diff --git a/lib/rspec/matchers/built_in/change.rb b/lib/rspec/matchers/built_in/change.rb index 387d0e7ab..2641c19a4 100644 --- a/lib/rspec/matchers/built_in/change.rb +++ b/lib/rspec/matchers/built_in/change.rb @@ -77,11 +77,6 @@ def supports_block_expectations? true end - # @private - def supports_value_expectations? - false - end - private def initialize(receiver=nil, message=nil, &block) @@ -112,10 +107,12 @@ def raise_block_syntax_error end def positive_failure_reason + return "was not given a block" unless Proc === @event_proc "is still #{@actual_before_description}" end def negative_failure_reason + return "was not given a block" unless Proc === @event_proc "did change from #{@actual_before_description} " \ "to #{description_of change_details.actual_after}" end @@ -161,14 +158,10 @@ def supports_block_expectations? true end - # @private - def supports_value_expectations? - false - end - private def failure_reason + return "was not given a block" unless Proc === @event_proc "was changed by #{description_of @change_details.actual_delta}" end end @@ -197,6 +190,7 @@ def description # @private def failure_message + return not_given_a_block_failure unless Proc === @event_proc return before_value_failure unless @matches_before return did_not_change_failure unless @change_details.changed? after_value_failure @@ -207,11 +201,6 @@ def supports_block_expectations? true end - # @private - def supports_value_expectations? - false - end - private def perform_change(event_proc) @@ -253,6 +242,11 @@ def did_change_failure "did change from #{@actual_before_description} " \ "to #{description_of @change_details.actual_after}" end + + def not_given_a_block_failure + "expected #{@change_details.value_representation} to have changed " \ + "#{change_description}, but was not given a block" + end end # @api private @@ -284,6 +278,7 @@ def does_not_match?(event_proc) # @private def failure_message_when_negated + return not_given_a_block_failure unless Proc === @event_proc return before_value_failure unless @matches_before did_change_failure end diff --git a/lib/rspec/matchers/built_in/compound.rb b/lib/rspec/matchers/built_in/compound.rb index 56f27b1a6..97f05dd61 100644 --- a/lib/rspec/matchers/built_in/compound.rb +++ b/lib/rspec/matchers/built_in/compound.rb @@ -26,19 +26,11 @@ def description "#{matcher_1.description} #{conjunction} #{matcher_2.description}" end - # @api private def supports_block_expectations? matcher_supports_block_expectations?(matcher_1) && matcher_supports_block_expectations?(matcher_2) end - # @api private - def supports_value_expectations? - matcher_supports_value_expectations?(matcher_1) && - matcher_supports_value_expectations?(matcher_2) - end - - # @api private def expects_call_stack_jump? NestedEvaluator.matcher_expects_call_stack_jump?(matcher_1) || NestedEvaluator.matcher_expects_call_stack_jump?(matcher_2) @@ -110,12 +102,6 @@ def matcher_supports_block_expectations?(matcher) false end - def matcher_supports_value_expectations?(matcher) - matcher.supports_value_expectations? - rescue NoMethodError - true - end - def matcher_is_diffable?(matcher) matcher.diffable? rescue NoMethodError diff --git a/lib/rspec/matchers/built_in/output.rb b/lib/rspec/matchers/built_in/output.rb index 449e44b54..be100a26e 100644 --- a/lib/rspec/matchers/built_in/output.rb +++ b/lib/rspec/matchers/built_in/output.rb @@ -94,13 +94,6 @@ def supports_block_expectations? true end - # @api private - # Indicates this matcher matches against a block only. - # @return [False] - def supports_value_expectations? - false - end - private def captured? @@ -108,11 +101,13 @@ def captured? end def positive_failure_reason + return "was not a block" unless Proc === @block return "output #{actual_output_description}" if @expected "did not" end def negative_failure_reason + return "was not a block" unless Proc === @block "output #{actual_output_description}" end diff --git a/lib/rspec/matchers/built_in/raise_error.rb b/lib/rspec/matchers/built_in/raise_error.rb index 0136e2603..6427d030a 100644 --- a/lib/rspec/matchers/built_in/raise_error.rb +++ b/lib/rspec/matchers/built_in/raise_error.rb @@ -76,12 +76,6 @@ def supports_block_expectations? true end - # @private - def supports_value_expectations? - false - end - - # @private def expects_call_stack_jump? true end @@ -205,6 +199,7 @@ def format_backtrace(backtrace) end def given_error + return " but was not given a block" unless Proc === @given_proc return " but nothing was raised" unless @actual_error backtrace = format_backtrace(@actual_error.backtrace) diff --git a/lib/rspec/matchers/built_in/throw_symbol.rb b/lib/rspec/matchers/built_in/throw_symbol.rb index e94792a1f..1b6b8bcb4 100644 --- a/lib/rspec/matchers/built_in/throw_symbol.rb +++ b/lib/rspec/matchers/built_in/throw_symbol.rb @@ -88,16 +88,12 @@ def description end # @api private + # Indicates this matcher matches against a block. + # @return [True] def supports_block_expectations? true end - # @api private - def supports_value_expectations? - false - end - - # @api private def expects_call_stack_jump? true end @@ -105,6 +101,7 @@ def expects_call_stack_jump? private def actual_result + return "but was not a block" unless Proc === @block "got #{caught}" end diff --git a/lib/rspec/matchers/built_in/yield.rb b/lib/rspec/matchers/built_in/yield.rb index cac2bb433..929fef1ec 100644 --- a/lib/rspec/matchers/built_in/yield.rb +++ b/lib/rspec/matchers/built_in/yield.rb @@ -10,6 +10,7 @@ module BuiltIn class YieldProbe def self.probe(block, &callback) probe = new(block, &callback) + return probe unless probe.has_block? probe.probe end @@ -23,6 +24,10 @@ def initialize(block, &callback) self.yielded_args = [] end + def has_block? + Proc === @block + end + def probe assert_valid_expect_block! @block.call(self) @@ -147,12 +152,14 @@ def times # @private def matches?(block) @probe = YieldProbe.probe(block) + return false unless @probe.has_block? + @probe.num_yields.__send__(@expectation_type, @expected_yields_count) end # @private def does_not_match?(block) - !matches?(block) + !matches?(block) && @probe.has_block? end # @api private @@ -172,11 +179,6 @@ def supports_block_expectations? true end - # @private - def supports_value_expectations? - false - end - private def set_expected_yields_count(relativity, n) @@ -190,6 +192,7 @@ def set_expected_yields_count(relativity, n) end def failure_reason + return ' but was not a block' unless @probe.has_block? return '' unless @expected_yields_count " #{human_readable_expectation_type}#{human_readable_count(@expected_yields_count)}" \ " but yielded #{human_readable_count(@probe.num_yields)}" @@ -219,12 +222,13 @@ class YieldWithNoArgs < BaseMatcher # @private def matches?(block) @probe = YieldProbe.probe(block) + return false unless @probe.has_block? @probe.yielded_once?(:yield_with_no_args) && @probe.single_yield_args.empty? end # @private def does_not_match?(block) - !matches?(block) + !matches?(block) && @probe.has_block? end # @private @@ -242,19 +246,16 @@ def supports_block_expectations? true end - # @private - def supports_value_expectations? - false - end - private def positive_failure_reason + return 'was not a block' unless @probe.has_block? return 'did not yield' if @probe.num_yields.zero? "yielded with arguments: #{description_of @probe.single_yield_args}" end def negative_failure_reason + return 'was not a block' unless @probe.has_block? 'did' end end @@ -275,13 +276,14 @@ def matches?(block) @actual_formatted = actual_formatted @args_matched_when_yielded &&= args_currently_match? end + return false unless @probe.has_block? @probe.probe @probe.yielded_once?(:yield_with_args) && @args_matched_when_yielded end # @private def does_not_match?(block) - !matches?(block) + !matches?(block) && @probe.has_block? end # @private @@ -306,14 +308,10 @@ def supports_block_expectations? true end - # @private - def supports_value_expectations? - false - end - private def positive_failure_reason + return 'was not a block' unless @probe.has_block? return 'did not yield' if @probe.num_yields.zero? @positive_args_failure end @@ -323,7 +321,9 @@ def expected_arg_description end def negative_failure_reason - if @args_matched_when_yielded && !@expected.empty? + if !@probe.has_block? + 'was not a block' + elsif @args_matched_when_yielded && !@expected.empty? 'yielded with expected arguments' \ "\nexpected not: #{surface_descriptions_in(@expected).inspect}" \ "\n got: #{@actual_formatted}" @@ -375,11 +375,12 @@ def matches?(block) yield_count += 1 end + return false unless @probe.has_block? args_matched_when_yielded && yield_count == @expected.length end def does_not_match?(block) - !matches?(block) + !matches?(block) && @probe.has_block? end # @private @@ -404,11 +405,6 @@ def supports_block_expectations? true end - # @private - def supports_value_expectations? - false - end - private def expected_arg_description @@ -416,12 +412,16 @@ def expected_arg_description end def positive_failure_reason + return 'was not a block' unless @probe.has_block? + 'yielded with unexpected arguments' \ "\nexpected: #{surface_descriptions_in(@expected).inspect}" \ "\n got: [#{@actual_formatted.join(", ")}]" end def negative_failure_reason + return 'was not a block' unless @probe.has_block? + 'yielded with expected arguments' \ "\nexpected not: #{surface_descriptions_in(@expected).inspect}" \ "\n got: [#{@actual_formatted.join(", ")}]" diff --git a/lib/rspec/matchers/dsl.rb b/lib/rspec/matchers/dsl.rb index df5c49c0f..f185d9117 100644 --- a/lib/rspec/matchers/dsl.rb +++ b/lib/rspec/matchers/dsl.rb @@ -401,10 +401,6 @@ def supports_block_expectations? false end - def supports_value_expectations? - true - end - # Most matchers do not expect call stack jumps. def expects_call_stack_jump? false diff --git a/lib/rspec/matchers/matcher_protocol.rb b/lib/rspec/matchers/matcher_protocol.rb index 4a87f6481..c5bc432e1 100644 --- a/lib/rspec/matchers/matcher_protocol.rb +++ b/lib/rspec/matchers/matcher_protocol.rb @@ -60,12 +60,6 @@ class MatcherProtocol # @return [Boolean] true if this matcher can be used in block expressions. # @note If not defined, RSpec assumes a value of `false` for this method. - # @!method supports_value_expectations? - # Indicates that this matcher can be used in a value expectation expression, - # such as `expect(foo).to eq(bar)`. - # @return [Boolean] true if this matcher can be used in value expressions. - # @note If not defined, RSpec assumes a value of `true` for this method. - # @!method expects_call_stack_jump? # Indicates that when this matcher is used in a block expectation # expression, it expects the block to use a ruby construct that causes diff --git a/spec/rspec/expectations/extensions/kernel_spec.rb b/spec/rspec/expectations/extensions/kernel_spec.rb index a57e7de1c..00c68a8c4 100644 --- a/spec/rspec/expectations/extensions/kernel_spec.rb +++ b/spec/rspec/expectations/extensions/kernel_spec.rb @@ -1,7 +1,7 @@ RSpec.describe Object, "#should" do before(:example) do @target = "target" - @matcher = double("matcher", :supports_value_expectations? => true) + @matcher = double("matcher") allow(@matcher).to receive(:matches?).and_return(true) allow(@matcher).to receive(:failure_message) end @@ -59,7 +59,7 @@ def method_missing(name, *args) RSpec.describe Object, "#should_not" do before(:example) do @target = "target" - @matcher = double("matcher", :supports_value_expectations? => true) + @matcher = double("matcher") end it "accepts and interacts with a matcher" do diff --git a/spec/rspec/matchers/aliased_matcher_spec.rb b/spec/rspec/matchers/aliased_matcher_spec.rb index 612f3a4d5..0902ecfeb 100644 --- a/spec/rspec/matchers/aliased_matcher_spec.rb +++ b/spec/rspec/matchers/aliased_matcher_spec.rb @@ -14,7 +14,7 @@ def description end RSpec::Matchers.alias_matcher :alias_of_my_base_matcher, :my_base_matcher - it_behaves_like "an RSpec value matcher", :valid_value => 13, :invalid_value => nil do + it_behaves_like "an RSpec matcher", :valid_value => 13, :invalid_value => nil do let(:matcher) { alias_of_my_base_matcher } end diff --git a/spec/rspec/matchers/built_in/all_spec.rb b/spec/rspec/matchers/built_in/all_spec.rb index be7231e37..9ef463cec 100644 --- a/spec/rspec/matchers/built_in/all_spec.rb +++ b/spec/rspec/matchers/built_in/all_spec.rb @@ -1,7 +1,7 @@ module RSpec::Matchers::BuiltIn RSpec.describe All do - it_behaves_like 'an RSpec value matcher', :valid_value => ['A', 'A', 'A'], :invalid_value => ['A', 'A', 'B'], :disallows_negation => true do + it_behaves_like 'an RSpec matcher', :valid_value => ['A', 'A', 'A'], :invalid_value => ['A', 'A', 'B'], :disallows_negation => true do let(:matcher) { all( eq('A') ) } end diff --git a/spec/rspec/matchers/built_in/be_between_spec.rb b/spec/rspec/matchers/built_in/be_between_spec.rb index c6a218269..f7aaaf978 100644 --- a/spec/rspec/matchers/built_in/be_between_spec.rb +++ b/spec/rspec/matchers/built_in/be_between_spec.rb @@ -80,7 +80,7 @@ def inspect end end - it_behaves_like "an RSpec value matcher", :valid_value => (10), :invalid_value => (11) do + it_behaves_like "an RSpec matcher", :valid_value => (10), :invalid_value => (11) do let(:matcher) { be_between(1, 10) } end diff --git a/spec/rspec/matchers/built_in/be_instance_of_spec.rb b/spec/rspec/matchers/built_in/be_instance_of_spec.rb index d3864969c..ac2252e73 100644 --- a/spec/rspec/matchers/built_in/be_instance_of_spec.rb +++ b/spec/rspec/matchers/built_in/be_instance_of_spec.rb @@ -2,7 +2,7 @@ module RSpec module Matchers [:be_an_instance_of, :be_instance_of].each do |method| RSpec.describe "expect(actual).to #{method}(expected)" do - it_behaves_like "an RSpec value matcher", :valid_value => "a", :invalid_value => 5 do + it_behaves_like "an RSpec matcher", :valid_value => "a", :invalid_value => 5 do let(:matcher) { send(method, String) } end diff --git a/spec/rspec/matchers/built_in/be_kind_of_spec.rb b/spec/rspec/matchers/built_in/be_kind_of_spec.rb index 497525f06..4d479c4f9 100644 --- a/spec/rspec/matchers/built_in/be_kind_of_spec.rb +++ b/spec/rspec/matchers/built_in/be_kind_of_spec.rb @@ -2,7 +2,7 @@ module RSpec module Matchers [:be_a_kind_of, :be_kind_of].each do |method| RSpec.describe "expect(actual).to #{method}(expected)" do - it_behaves_like "an RSpec value matcher", :valid_value => 5, :invalid_value => "a" do + it_behaves_like "an RSpec matcher", :valid_value => 5, :invalid_value => "a" do let(:matcher) { send(method, Integer) } end diff --git a/spec/rspec/matchers/built_in/be_within_spec.rb b/spec/rspec/matchers/built_in/be_within_spec.rb index ecf8b86e9..72ca624cb 100644 --- a/spec/rspec/matchers/built_in/be_within_spec.rb +++ b/spec/rspec/matchers/built_in/be_within_spec.rb @@ -1,7 +1,7 @@ module RSpec module Matchers RSpec.describe "expect(actual).to be_within(delta).of(expected)" do - it_behaves_like "an RSpec value matcher", :valid_value => 5, :invalid_value => -5 do + it_behaves_like "an RSpec matcher", :valid_value => 5, :invalid_value => -5 do let(:matcher) { be_within(2).of(4.0) } end diff --git a/spec/rspec/matchers/built_in/change_spec.rb b/spec/rspec/matchers/built_in/change_spec.rb index 04ebd6902..dbcba5b9d 100644 --- a/spec/rspec/matchers/built_in/change_spec.rb +++ b/spec/rspec/matchers/built_in/change_spec.rb @@ -1044,51 +1044,39 @@ def @instance.send(*_args); raise "DOH! Library developers shouldn't use #send!" }.not_to raise_error end - it_behaves_like "an RSpec block-only matcher" do - let(:matcher) { change { @k } } - before { @k = 1 } - def valid_block - @k += 1 - end - def invalid_block - end + k = 1 + before { k = 1 } + it_behaves_like "an RSpec matcher", :valid_value => lambda { k += 1 }, + :invalid_value => lambda { } do + let(:matcher) { change { k } } end end RSpec.describe RSpec::Matchers::BuiltIn::ChangeRelatively do - it_behaves_like "an RSpec block-only matcher", :disallows_negation => true do - let(:matcher) { change { @k }.by(1) } - before { @k = 0 } - def valid_block - @k += 1 - end - def invalid_block - @k += 2 - end + k = 0 + before { k = 0 } + it_behaves_like "an RSpec matcher", :valid_value => lambda { k += 1 }, + :invalid_value => lambda { k += 2 }, + :disallows_negation => true do + let(:matcher) { change { k }.by(1) } end end RSpec.describe RSpec::Matchers::BuiltIn::ChangeFromValue do - it_behaves_like "an RSpec block-only matcher" do - let(:matcher) { change { @k }.from(0) } - before { @k = 0 } - def valid_block - @k += 1 - end - def invalid_block - end + k = 0 + before { k = 0 } + it_behaves_like "an RSpec matcher", :valid_value => lambda { k += 1 }, + :invalid_value => lambda { } do + let(:matcher) { change { k }.from(0) } end end RSpec.describe RSpec::Matchers::BuiltIn::ChangeToValue do - it_behaves_like "an RSpec block-only matcher", :disallows_negation => true do - let(:matcher) { change { @k }.to(2) } - before { @k = 0 } - def valid_block - @k = 2 - end - def invalid_block - @k = 3 - end + k = 0 + before { k = 0 } + it_behaves_like "an RSpec matcher", :valid_value => lambda { k = 2 }, + :invalid_value => lambda { k = 3 }, + :disallows_negation => true do + let(:matcher) { change { k }.to(2) } end end diff --git a/spec/rspec/matchers/built_in/compound_spec.rb b/spec/rspec/matchers/built_in/compound_spec.rb index 0594b6b0a..fadf6ed37 100644 --- a/spec/rspec/matchers/built_in/compound_spec.rb +++ b/spec/rspec/matchers/built_in/compound_spec.rb @@ -262,12 +262,12 @@ def expect_block describe "expect(...).to matcher.and(other_matcher)" do - it_behaves_like "an RSpec value matcher", :valid_value => 3, :invalid_value => 4, :disallows_negation => true do + it_behaves_like "an RSpec matcher", :valid_value => 3, :invalid_value => 4, :disallows_negation => true do let(:matcher) { eq(3).and be <= 3 } end context 'when using boolean AND `&` alias' do - it_behaves_like "an RSpec value matcher", :valid_value => 3, :invalid_value => 4, :disallows_negation => true do + it_behaves_like "an RSpec matcher", :valid_value => 3, :invalid_value => 4, :disallows_negation => true do let(:matcher) { eq(3) & be_a(Integer) } end end @@ -570,12 +570,12 @@ def expect_block end describe "expect(...).to matcher.or(other_matcher)" do - it_behaves_like "an RSpec value matcher", :valid_value => 3, :invalid_value => 5, :disallows_negation => true do + it_behaves_like "an RSpec matcher", :valid_value => 3, :invalid_value => 5, :disallows_negation => true do let(:matcher) { eq(3).or eq(4) } end context 'when using boolean OR `|` alias' do - it_behaves_like "an RSpec value matcher", :valid_value => 3, :invalid_value => 5, :disallows_negation => true do + it_behaves_like "an RSpec matcher", :valid_value => 3, :invalid_value => 5, :disallows_negation => true do let(:matcher) { eq(3) | eq(4) } end end diff --git a/spec/rspec/matchers/built_in/contain_exactly_spec.rb b/spec/rspec/matchers/built_in/contain_exactly_spec.rb index ac1996ae8..075d7469c 100644 --- a/spec/rspec/matchers/built_in/contain_exactly_spec.rb +++ b/spec/rspec/matchers/built_in/contain_exactly_spec.rb @@ -148,7 +148,7 @@ def array.send; :sent; end end RSpec.describe "expect(array).to contain_exactly(*other_array)" do - it_behaves_like "an RSpec value matcher", :valid_value => [1, 2], :invalid_value => [1] do + it_behaves_like "an RSpec matcher", :valid_value => [1, 2], :invalid_value => [1] do let(:matcher) { contain_exactly(2, 1) } end diff --git a/spec/rspec/matchers/built_in/cover_spec.rb b/spec/rspec/matchers/built_in/cover_spec.rb index ff73fb80c..4e91e9336 100644 --- a/spec/rspec/matchers/built_in/cover_spec.rb +++ b/spec/rspec/matchers/built_in/cover_spec.rb @@ -1,6 +1,6 @@ if (1..2).respond_to?(:cover?) RSpec.describe "expect(...).to cover(expected)" do - it_behaves_like "an RSpec value matcher", :valid_value => (1..10), :invalid_value => (20..30) do + it_behaves_like "an RSpec matcher", :valid_value => (1..10), :invalid_value => (20..30) do let(:matcher) { cover(5) } end diff --git a/spec/rspec/matchers/built_in/eq_spec.rb b/spec/rspec/matchers/built_in/eq_spec.rb index fd7886d03..48cc4576f 100644 --- a/spec/rspec/matchers/built_in/eq_spec.rb +++ b/spec/rspec/matchers/built_in/eq_spec.rb @@ -1,7 +1,7 @@ module RSpec module Matchers RSpec.describe "eq" do - it_behaves_like "an RSpec value matcher", :valid_value => 1, :invalid_value => 2 do + it_behaves_like "an RSpec matcher", :valid_value => 1, :invalid_value => 2 do let(:matcher) { eq(1) } end diff --git a/spec/rspec/matchers/built_in/eql_spec.rb b/spec/rspec/matchers/built_in/eql_spec.rb index d07b14189..4491fc9e8 100644 --- a/spec/rspec/matchers/built_in/eql_spec.rb +++ b/spec/rspec/matchers/built_in/eql_spec.rb @@ -1,7 +1,7 @@ module RSpec module Matchers RSpec.describe "eql" do - it_behaves_like "an RSpec value matcher", :valid_value => 1, :invalid_value => 2 do + it_behaves_like "an RSpec matcher", :valid_value => 1, :invalid_value => 2 do let(:matcher) { eql(1) } end diff --git a/spec/rspec/matchers/built_in/equal_spec.rb b/spec/rspec/matchers/built_in/equal_spec.rb index 94c1db7a9..f56006d61 100644 --- a/spec/rspec/matchers/built_in/equal_spec.rb +++ b/spec/rspec/matchers/built_in/equal_spec.rb @@ -1,7 +1,7 @@ module RSpec module Matchers RSpec.describe "equal" do - it_behaves_like "an RSpec value matcher", :valid_value => :a, :invalid_value => :b do + it_behaves_like "an RSpec matcher", :valid_value => :a, :invalid_value => :b do let(:matcher) { equal(:a) } end diff --git a/spec/rspec/matchers/built_in/exist_spec.rb b/spec/rspec/matchers/built_in/exist_spec.rb index 2d2c4e5bc..cecfea445 100644 --- a/spec/rspec/matchers/built_in/exist_spec.rb +++ b/spec/rspec/matchers/built_in/exist_spec.rb @@ -1,6 +1,6 @@ RSpec.describe "exist matcher" do - it_behaves_like "an RSpec value matcher", :valid_value => Class.new { def exist?; true; end }.new, - :invalid_value => Class.new { def exist?; false; end }.new do + it_behaves_like "an RSpec matcher", :valid_value => Class.new { def exist?; true; end }.new, + :invalid_value => Class.new { def exist?; false; end }.new do let(:matcher) { exist } end diff --git a/spec/rspec/matchers/built_in/has_spec.rb b/spec/rspec/matchers/built_in/has_spec.rb index 8a6b58836..fb740f7dd 100644 --- a/spec/rspec/matchers/built_in/has_spec.rb +++ b/spec/rspec/matchers/built_in/has_spec.rb @@ -1,6 +1,6 @@ RSpec.describe "expect(...).to have_sym(*args)" do - it_behaves_like "an RSpec value matcher", :valid_value => { :a => 1 }, - :invalid_value => {} do + it_behaves_like "an RSpec matcher", :valid_value => { :a => 1 }, + :invalid_value => {} do let(:matcher) { have_key(:a) } end diff --git a/spec/rspec/matchers/built_in/have_attributes_spec.rb b/spec/rspec/matchers/built_in/have_attributes_spec.rb index f92680c3f..f97b64b98 100644 --- a/spec/rspec/matchers/built_in/have_attributes_spec.rb +++ b/spec/rspec/matchers/built_in/have_attributes_spec.rb @@ -22,7 +22,7 @@ def parent(parent_name) describe "expect(...).to have_attributes(with_one_attribute)" do - it_behaves_like "an RSpec value matcher", :valid_value => Person.new("Correct name", 33), :invalid_value => Person.new("Wrong Name", 11) do + it_behaves_like "an RSpec matcher", :valid_value => Person.new("Correct name", 33), :invalid_value => Person.new("Wrong Name", 11) do let(:matcher) { have_attributes(:name => "Correct name") } end @@ -132,7 +132,7 @@ def count describe "expect(...).to have_attributes(with_multiple_attributes)" do - it_behaves_like "an RSpec value matcher", :valid_value => Person.new("Correct name", 33), :invalid_value => Person.new("Wrong Name", 11) do + it_behaves_like "an RSpec matcher", :valid_value => Person.new("Correct name", 33), :invalid_value => Person.new("Wrong Name", 11) do let(:matcher) { have_attributes(:name => "Correct name", :age => 33) } end diff --git a/spec/rspec/matchers/built_in/include_spec.rb b/spec/rspec/matchers/built_in/include_spec.rb index da1fe6c90..7179e1529 100644 --- a/spec/rspec/matchers/built_in/include_spec.rb +++ b/spec/rspec/matchers/built_in/include_spec.rb @@ -103,7 +103,7 @@ def hash.send; :sent; end end describe "expect(...).to include(with_one_arg)" do - it_behaves_like "an RSpec value matcher", :valid_value => [1, 2], :invalid_value => [1] do + it_behaves_like "an RSpec matcher", :valid_value => [1, 2], :invalid_value => [1] do let(:matcher) { include(2) } end diff --git a/spec/rspec/matchers/built_in/match_spec.rb b/spec/rspec/matchers/built_in/match_spec.rb index 4373d4489..1f88ef69d 100644 --- a/spec/rspec/matchers/built_in/match_spec.rb +++ b/spec/rspec/matchers/built_in/match_spec.rb @@ -1,5 +1,5 @@ RSpec.describe "expect(...).to match(expected)" do - it_behaves_like "an RSpec value matcher", :valid_value => 'ab', :invalid_value => 'bc' do + it_behaves_like "an RSpec matcher", :valid_value => 'ab', :invalid_value => 'bc' do let(:matcher) { match(/a/) } end diff --git a/spec/rspec/matchers/built_in/output_spec.rb b/spec/rspec/matchers/built_in/output_spec.rb index 07f9e8979..f51f4c562 100644 --- a/spec/rspec/matchers/built_in/output_spec.rb +++ b/spec/rspec/matchers/built_in/output_spec.rb @@ -2,13 +2,8 @@ include helper_module extend helper_module - it_behaves_like "an RSpec block-only matcher" do + it_behaves_like("an RSpec matcher", :valid_value => lambda { print_to_stream('foo') }, :invalid_value => lambda {}) do let(:matcher) { output(/fo/).send(matcher_method) } - def valid_block - print_to_stream('foo') - end - def invalid_block - end end define_method :matcher do |*args| diff --git a/spec/rspec/matchers/built_in/raise_error_spec.rb b/spec/rspec/matchers/built_in/raise_error_spec.rb index 6929bf9d2..67fc014db 100644 --- a/spec/rspec/matchers/built_in/raise_error_spec.rb +++ b/spec/rspec/matchers/built_in/raise_error_spec.rb @@ -1,10 +1,6 @@ RSpec.describe "expect { ... }.to raise_error" do - it_behaves_like "an RSpec block-only matcher" do - def valid_block - raise "boom" - end - def invalid_block - end + it_behaves_like("an RSpec matcher", :valid_value => lambda { raise "boom" }, + :invalid_value => lambda { }) do let(:matcher) { raise_error Exception } end diff --git a/spec/rspec/matchers/built_in/respond_to_spec.rb b/spec/rspec/matchers/built_in/respond_to_spec.rb index 5837dd3b1..4ee1b0ce3 100644 --- a/spec/rspec/matchers/built_in/respond_to_spec.rb +++ b/spec/rspec/matchers/built_in/respond_to_spec.rb @@ -1,5 +1,5 @@ RSpec.describe "expect(...).to respond_to(:sym)" do - it_behaves_like "an RSpec value matcher", :valid_value => "s", :invalid_value => 5 do + it_behaves_like "an RSpec matcher", :valid_value => "s", :invalid_value => 5 do let(:matcher) { respond_to(:upcase) } end diff --git a/spec/rspec/matchers/built_in/satisfy_spec.rb b/spec/rspec/matchers/built_in/satisfy_spec.rb index f7748ecc7..6d6b7f174 100644 --- a/spec/rspec/matchers/built_in/satisfy_spec.rb +++ b/spec/rspec/matchers/built_in/satisfy_spec.rb @@ -1,5 +1,5 @@ RSpec.describe "expect(...).to satisfy { block }" do - it_behaves_like "an RSpec value matcher", :valid_value => true, :invalid_value => false do + it_behaves_like "an RSpec matcher", :valid_value => true, :invalid_value => false do let(:matcher) { satisfy { |v| v } } end diff --git a/spec/rspec/matchers/built_in/start_and_end_with_spec.rb b/spec/rspec/matchers/built_in/start_and_end_with_spec.rb index b063f199d..77dcf43b7 100644 --- a/spec/rspec/matchers/built_in/start_and_end_with_spec.rb +++ b/spec/rspec/matchers/built_in/start_and_end_with_spec.rb @@ -1,5 +1,5 @@ RSpec.describe "expect(...).to start_with" do - it_behaves_like "an RSpec value matcher", :valid_value => "ab", :invalid_value => "bc" do + it_behaves_like "an RSpec matcher", :valid_value => "ab", :invalid_value => "bc" do let(:matcher) { start_with("a") } end @@ -207,7 +207,7 @@ def ==(other) end RSpec.describe "expect(...).to end_with" do - it_behaves_like "an RSpec value matcher", :valid_value => "ab", :invalid_value => "bc" do + it_behaves_like "an RSpec matcher", :valid_value => "ab", :invalid_value => "bc" do let(:matcher) { end_with("b") } end diff --git a/spec/rspec/matchers/built_in/throw_symbol_spec.rb b/spec/rspec/matchers/built_in/throw_symbol_spec.rb index d9300f6dc..75da4d806 100644 --- a/spec/rspec/matchers/built_in/throw_symbol_spec.rb +++ b/spec/rspec/matchers/built_in/throw_symbol_spec.rb @@ -1,11 +1,7 @@ module RSpec::Matchers::BuiltIn RSpec.describe ThrowSymbol do - it_behaves_like "an RSpec block-only matcher" do - def valid_block - throw :foo - end - def invalid_block - end + it_behaves_like("an RSpec matcher", :valid_value => lambda { throw :foo }, + :invalid_value => lambda { }) do let(:matcher) { throw_symbol(:foo) } end diff --git a/spec/rspec/matchers/built_in/yield_spec.rb b/spec/rspec/matchers/built_in/yield_spec.rb index d6fe1ef33..1b70c8c8f 100644 --- a/spec/rspec/matchers/built_in/yield_spec.rb +++ b/spec/rspec/matchers/built_in/yield_spec.rb @@ -29,36 +29,15 @@ def each_arg(*args, &block) end end -# NOTE: `yield` passes a probe to expect an that probe should be passed -# to expectation target. This is different from the other block matchers. -# Due to strict requirement in Ruby 1.8 to call a block with arguments if -# the block is declared to accept them. To work around this limitation, -# this example group overrides the default definition of expectations -# and lambdas that take the expectation target in a way that they accept -# a probe. -RSpec.shared_examples "an RSpec probe-yielding block-only matcher" do |*options| - include_examples "an RSpec block-only matcher", *options do - let(:valid_expectation) { expect { |block| valid_block(&block) } } - let(:invalid_expectation) { expect { |block| invalid_block(&block) } } - - let(:valid_block_lambda) { lambda { |block| valid_block(&block) } } - let(:invalid_block_lambda) { lambda { |block| invalid_block(&block) } } - end -end - RSpec.describe "yield_control matcher" do include YieldHelpers extend YieldHelpers - it_behaves_like "an RSpec probe-yielding block-only matcher", + it_behaves_like "an RSpec matcher", + :valid_value => lambda { |b| _yield_with_no_args(&b) }, + :invalid_value => lambda { |b| _dont_yield(&b) }, :failure_message_uses_no_inspect => true do let(:matcher) { yield_control } - def valid_block(&block) - _yield_with_no_args(&block) - end - def invalid_block(&block) - _dont_yield(&block) - end end it 'has a description' do @@ -223,14 +202,10 @@ def invalid_block(&block) include YieldHelpers extend YieldHelpers - it_behaves_like "an RSpec probe-yielding block-only matcher" do + it_behaves_like "an RSpec matcher", + :valid_value => lambda { |b| _yield_with_no_args(&b) }, + :invalid_value => lambda { |b| _yield_with_args(1, &b) } do let(:matcher) { yield_with_no_args } - def valid_block(&block) - _yield_with_no_args(&block) - end - def invalid_block(&block) - _yield_with_args(1, &block) - end end it 'has a description' do @@ -310,14 +285,10 @@ def invalid_block(&block) include YieldHelpers extend YieldHelpers - it_behaves_like "an RSpec probe-yielding block-only matcher" do + it_behaves_like "an RSpec matcher", + :valid_value => lambda { |b| _yield_with_args(1, &b) }, + :invalid_value => lambda { |b| _yield_with_args(2, &b) } do let(:matcher) { yield_with_args(1) } - def valid_block(&block) - _yield_with_args(1, &block) - end - def invalid_block(&block) - _yield_with_args(2, &block) - end end it 'has a description' do @@ -574,14 +545,10 @@ def invalid_block(&block) include YieldHelpers extend YieldHelpers - it_behaves_like "an RSpec probe-yielding block-only matcher" do + it_behaves_like "an RSpec matcher", + :valid_value => lambda { |b| [1, 2].each(&b) }, + :invalid_value => lambda { |b| [3, 4].each(&b) } do let(:matcher) { yield_successive_args(1, 2) } - def valid_block(&block) - [1, 2].each(&block) - end - def invalid_block(&block) - [3, 4].each(&block) - end end it 'has a description' do diff --git a/spec/rspec/matchers/define_negated_matcher_spec.rb b/spec/rspec/matchers/define_negated_matcher_spec.rb index 3123847e7..b6f0e0ad7 100644 --- a/spec/rspec/matchers/define_negated_matcher_spec.rb +++ b/spec/rspec/matchers/define_negated_matcher_spec.rb @@ -45,7 +45,7 @@ def description include_examples "making a copy", :clone RSpec::Matchers.define_negated_matcher :an_array_excluding, :include - it_behaves_like "an RSpec value matcher", :valid_value => [1, 3], :invalid_value => [1, 2] do + it_behaves_like "an RSpec matcher", :valid_value => [1, 3], :invalid_value => [1, 2] do let(:matcher) { an_array_excluding(2) } end diff --git a/spec/rspec/matchers/dsl_spec.rb b/spec/rspec/matchers/dsl_spec.rb index 4063df930..d34f2ee87 100644 --- a/spec/rspec/matchers/dsl_spec.rb +++ b/spec/rspec/matchers/dsl_spec.rb @@ -143,7 +143,7 @@ def new_matcher(name, *expected, &block) RSpec::Matchers::DSL::Matcher.new(name, block, self, *expected) end - it_behaves_like "an RSpec value matcher", :valid_value => 1, :invalid_value => 2 do + it_behaves_like "an RSpec matcher", :valid_value => 1, :invalid_value => 2 do let(:matcher) do new_matcher(:equal_to_1) do match { |v| v == 1 } diff --git a/spec/rspec/matchers_spec.rb b/spec/rspec/matchers_spec.rb index 681704fb0..a6e7e1b0f 100644 --- a/spec/rspec/matchers_spec.rb +++ b/spec/rspec/matchers_spec.rb @@ -119,9 +119,8 @@ module Matchers # This spec is merely to make sure we don't forget to make # a built-in matcher implement `===`. It doesn't check the - # semantics of that. Use the "an RSpec value matcher" and - # "an RSpec block matcher" shared example groups to actually - # check the semantics. + # semantics of that. Use the "an RSpec matcher" shared + # example group to actually check the semantics. expect(missing_threequals).to eq([]) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6b4efa71a..81769ab42 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,8 +5,8 @@ minimum_coverage 97 end -Dir['./spec/support/**/*.rb'].each do |f| - require f.sub(%r|\./spec/|, '') +Dir['./spec/support/**/*'].each do |f| + require f.sub(%r{\./spec/}, '') end module CommonHelperMethods diff --git a/spec/support/shared_examples.rb b/spec/support/shared_examples.rb new file mode 100644 index 000000000..aaca05806 --- /dev/null +++ b/spec/support/shared_examples.rb @@ -0,0 +1,111 @@ +RSpec.shared_examples "an RSpec matcher" do |options| + let(:valid_value) { options.fetch(:valid_value) } + let(:invalid_value) { options.fetch(:invalid_value) } + + # Note: do not use `matcher` in 2 expectation expressions in a single + # example here. In some cases (such as `change { }.to(2)`), it will fail + # because using it a second time will apply `x += 2` twice, changing + # the value to 4. + + it 'preserves the symmetric property of `==`' do + expect(matcher).to eq(matcher) + expect(matcher).not_to eq(valid_value) + expect(valid_value).not_to eq(matcher) + end + + it 'matches a valid value when using #=== so it can be composed' do + expect(matcher).to be === valid_value + end + + it 'does not match an invalid value when using #=== so it can be composed' do + expect(matcher).not_to be === invalid_value + end + + matcher :always_passes do + supports_block_expectations + match do |actual| + actual.call if Proc === actual + true + end + end + + matcher :always_fails do + supports_block_expectations + match do |actual| + actual.call if Proc === actual + false + end + end + + it 'allows additional matchers to be chained off it using `and`' do + expect(valid_value).to matcher.and always_passes + end + + it 'can be chained off of an existing matcher using `and`' do + expect(valid_value).to always_passes.and matcher + end + + it 'allows additional matchers to be chained off it using `or`' do + expect(valid_value).to matcher.or always_fails + end + + it 'can be chained off of an existing matcher using `or`' do + expect(valid_value).to always_fails.or matcher + end + + it 'implements the full matcher protocol' do + expect(matcher).to respond_to( + :matches?, + :failure_message, + :description, + :supports_block_expectations?, + :expects_call_stack_jump? + ) + + # We do not require failure_message_when_negated and does_not_match? + # Because some matchers purposefully do not support negation. + end + + it 'fails gracefully when given a value if it is a block matcher' do + if matcher.supports_block_expectations? + expect { + expect(3).to matcher + }.to fail_with(/was not( given)? a block/) + + unless options[:disallows_negation] + expect { + expect(3).not_to matcher + }.to fail_with(/was not( given)? a block/) + end + end + end + + it 'can be used in a composed matcher expression' do + expect([valid_value, invalid_value]).to include(matcher) + + expect { + expect([invalid_value]).to include(matcher) + }.to fail_including("include (#{matcher.description})") + end + + it 'can match negatively properly' do + unless options[:disallows_negation] + expect(invalid_value).not_to matcher + + expect { + expect(valid_value).not_to matcher + }.to fail + end + end + + it 'uses the `ObjectFormatter` for `failure_message`' do + allow(RSpec::Support::ObjectFormatter).to receive(:format).and_return("detailed inspect") + matcher.matches?(invalid_value) + message = matcher.failure_message + + # Undo our stub so it doesn't affect the `include` matcher below. + allow(RSpec::Support::ObjectFormatter).to receive(:format).and_call_original + expect(message).to include("detailed inspect") + end unless options[:failure_message_uses_no_inspect] +end + diff --git a/spec/support/shared_examples/block_matcher.rb b/spec/support/shared_examples/block_matcher.rb deleted file mode 100644 index 1e3c4207c..000000000 --- a/spec/support/shared_examples/block_matcher.rb +++ /dev/null @@ -1,80 +0,0 @@ -RSpec.shared_examples "an RSpec block-only matcher" do |*options| - # Note: Ruby 1.8 expects you to call a block with arguments if it is - # declared that accept arguments. In this case, some of the specs - # that include examples from this shared example group do not pass - # arguments. A workaround is to use splat and pick the first argument - # if it was passed. - options = options.first || {} - - # Note: do not use `matcher` in 2 expectation expressions in a single - # example here. In some cases (such as `change { x += 2 }.to(2)`), it - # will fail because using it a second time will apply `x += 2` twice, - # changing the value to 4. - - matcher :always_passes do - supports_block_expectations - match do |actual| - actual.call - true - end - end - - matcher :always_fails do - supports_block_expectations - match do |actual| - actual.call - false - end - end - - let(:valid_expectation) { expect { valid_block } } - let(:invalid_expectation) { expect { invalid_block } } - - let(:valid_block_lambda) { lambda { valid_block } } - let(:invalid_block_lambda) { lambda { invalid_block } } - - include_examples "an RSpec matcher", options - - it 'preserves the symmetric property of `==`' do - expect(matcher).to eq(matcher) - expect(matcher).not_to eq(valid_block_lambda) - expect(valid_block_lambda).not_to eq(matcher) - end - - it 'matches a valid block when using #=== so it can be composed' do - expect(matcher).to be === valid_block_lambda - end - - it 'does not match an invalid block when using #=== so it can be composed' do - expect(matcher).not_to be === invalid_block_lambda - end - - it 'matches a valid block when using #=== so it can be composed' do - expect(matcher).to be === valid_block_lambda - end - - it 'does not match an invalid block when using #=== so it can be composed' do - expect(matcher).not_to be === invalid_block_lambda - end - - it 'uses the `ObjectFormatter` for `failure_message`' do - allow(RSpec::Support::ObjectFormatter).to receive(:format).and_return("detailed inspect") - expect { invalid_expectation.to matcher }.to raise_error do |error| - # Undo our stub so it doesn't affect the `include` matcher below. - allow(RSpec::Support::ObjectFormatter).to receive(:format).and_call_original - expect(error.message).to include("detailed inspect") - end - end unless options[:failure_message_uses_no_inspect] - - it 'fails when given a value' do - expect { - expect(3).to matcher - }.to fail_with(/must pass a block rather than an argument/) - - unless options[:disallows_negation] - expect { - expect(3).not_to matcher - }.to fail_with(/must pass a block rather than an argument/) - end - end -end diff --git a/spec/support/shared_examples/matcher.rb b/spec/support/shared_examples/matcher.rb deleted file mode 100644 index ad255d361..000000000 --- a/spec/support/shared_examples/matcher.rb +++ /dev/null @@ -1,44 +0,0 @@ -RSpec.shared_examples "an RSpec matcher" do |options| - # Note: do not use `matcher` in 2 expectation expressions in a single - # example here. In some cases (such as `change { }.to(2)`), it will fail - # because using it a second time will apply `x += 2` twice, changing - # the value to 4. - - it 'allows additional matchers to be chained off it using `and`' do - valid_expectation.to matcher.and always_passes - end - - it 'can be chained off of an existing matcher using `and`' do - valid_expectation.to always_passes.and matcher - end - - it 'allows additional matchers to be chained off it using `or`' do - valid_expectation.to matcher.or always_fails - end - - it 'can be chained off of an existing matcher using `or`' do - valid_expectation.to always_fails.or matcher - end - - it 'implements the full matcher protocol' do - expect(matcher).to respond_to( - :matches?, - :failure_message, - :description, - :supports_block_expectations?, - :supports_value_expectations?, - :expects_call_stack_jump? - ) - - # We do not require failure_message_when_negated and does_not_match? - # Because some matchers purposefully do not support negation. - end - - it 'can match negatively properly' do - invalid_expectation.not_to matcher - - expect { - valid_expectation.not_to matcher - }.to fail - end unless options[:disallows_negation] -end diff --git a/spec/support/shared_examples/value_matcher.rb b/spec/support/shared_examples/value_matcher.rb deleted file mode 100644 index bcdbee9db..000000000 --- a/spec/support/shared_examples/value_matcher.rb +++ /dev/null @@ -1,66 +0,0 @@ -RSpec.shared_examples "an RSpec value matcher" do |options| - let(:valid_value) { options.fetch(:valid_value) } - let(:invalid_value) { options.fetch(:invalid_value) } - - matcher :always_passes do - match { |_actual| true } - end - - matcher :always_fails do - match { |_actual| false } - end - - def valid_expectation - expect(valid_value) - end - - def invalid_expectation - expect(invalid_value) - end - - include_examples "an RSpec matcher", options - - it 'preserves the symmetric property of `==`' do - expect(matcher).to eq(matcher) - expect(matcher).not_to eq(valid_value) - expect(valid_value).not_to eq(matcher) - end - - it 'matches a valid value when using #=== so it can be composed' do - expect(matcher).to be === valid_value - end - - it 'does not match an invalid value when using #=== so it can be composed' do - expect(matcher).not_to be === invalid_value - end - - it 'can be used in a composed matcher expression' do - expect([valid_value, invalid_value]).to include(matcher) - - expect { - expect([invalid_value]).to include(matcher) - }.to fail_including("include (#{matcher.description})") - end - - it 'uses the `ObjectFormatter` for `failure_message`' do - allow(RSpec::Support::ObjectFormatter).to receive(:format).and_return("detailed inspect") - matcher.matches?(invalid_value) - message = matcher.failure_message - - # Undo our stub so it doesn't affect the `include` matcher below. - allow(RSpec::Support::ObjectFormatter).to receive(:format).and_call_original - expect(message).to include("detailed inspect") - end - - it 'fails when given a block' do - expect { - expect { 2 + 2 }.to matcher - }.to fail_with(/must pass an argument rather than a block/) - - unless options[:disallows_negation] - expect { - expect { 2 + 2 }.not_to matcher - }.to fail_with(/must pass an argument rather than a block/) - end - end -end