diff --git a/rspec-expectations/Changelog.md b/rspec-expectations/Changelog.md index 84c112781..23953cbfe 100644 --- a/rspec-expectations/Changelog.md +++ b/rspec-expectations/Changelog.md @@ -11,6 +11,8 @@ Deprecations: `RSpec::Expectations::Configuration`. (Myron Marston) * Deprecate `be_xyz` predicate matcher on an object that doesn't respond to `xyz?` or `xyzs?`. (Daniel Fone) +* Deprecate `have_xyz` matcher on an object that doesn't respond to `has_xyz?`. + (Daniel Fone) ### 2.99.0.beta2 / 2014-02-17 [full changelog](http://github.com/rspec/rspec-expectations/compare/v2.99.0.beta1...v2.99.0.beta2) diff --git a/rspec-expectations/lib/rspec/matchers/built_in/has.rb b/rspec-expectations/lib/rspec/matchers/built_in/has.rb index aa05f8c99..8848be638 100644 --- a/rspec-expectations/lib/rspec/matchers/built_in/has.rb +++ b/rspec-expectations/lib/rspec/matchers/built_in/has.rb @@ -9,7 +9,10 @@ def initialize(expected, *args) end def matches?(actual) - actual.__send__(predicate(@expected), *@args) + method = predicate(@expected) + result = actual.__send__(method, *@args) + check_respond_to(actual, method) + result end def failure_message_for_should @@ -43,6 +46,13 @@ def failure_message_args_description desc = args_description "(#{desc})" if desc end + + def check_respond_to(actual, method) + RSpec.deprecate( + "Matching with #{@expected} on an object that doesn't respond to `#{method}`", + :replacement => "`respond_to_missing?` or `respond_to?` on your object" + ) unless actual.respond_to?(method) + end end end end diff --git a/rspec-expectations/spec/rspec/matchers/has_spec.rb b/rspec-expectations/spec/rspec/matchers/has_spec.rb index 15fc91d04..d89e42a65 100644 --- a/rspec-expectations/spec/rspec/matchers/has_spec.rb +++ b/rspec-expectations/spec/rspec/matchers/has_spec.rb @@ -57,6 +57,16 @@ def o.has_sym?(*args) expect(o).to have_sym(:foo) }.to raise_error("Funky exception") end + + it 'warns of deprecation when actual does not respond to #has_sym?' do + foo_class = Class.new do + def method_missing(method) + return true if method == :has_foo? + end + end + expect_deprecation_with_call_site(__FILE__, __LINE__ + 1, /Matching with have_foo on an object that doesn't respond to `has_foo\?`/) + expect(foo_class.new).to have_foo + end end describe "expect(...).not_to have_sym(*args)" do