Skip to content

Commit

Permalink
Improve the failure messages for the have/has predicate matcher.
Browse files Browse the repository at this point in the history
- For a case like `foo.should have_some_stuff`, "expected #has_some_stuff? to..." is better than "expected #has_some_stuff?(nil) to...".  The nil is confusing since you didn't pass any arguments.
- For a case like `foo.should have_each_of(:a, 7, "bar")`, "expected #has_each_of?(:a, 7, "bar")" is better than "expected #has_each_of?(:a)".  It's best to include all the arguments rather than just the first one.
  • Loading branch information
myronmarston committed Jun 11, 2011
1 parent 27c1205 commit e92aad9
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 8 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Expand Up @@ -4,6 +4,7 @@

* Enhancements
* HaveMatcher converts argument using #to_i (Alex Bepple & Pat Maddox)
* Improved failure message for the have_xxx matcher (Myron Marston)

### 2.6.0 / 2011-05-12

Expand Down
2 changes: 1 addition & 1 deletion features/built_in_matchers/predicates.feature
Expand Up @@ -101,7 +101,7 @@ Feature: predicate matchers
"""
When I run `rspec should_not_have_all_string_keys_spec.rb`
Then the output should contain "2 examples, 1 failure"
And the output should contain "expected #has_all_string_keys?(nil) to return false, got true"
And the output should contain "expected #has_all_string_keys? to return false, got true"

Scenario: matcher arguments are passed on to the predicate method
Given a file named "predicate_matcher_argument_spec.rb" with:
Expand Down
17 changes: 11 additions & 6 deletions lib/rspec/matchers/has.rb
Expand Up @@ -10,15 +10,15 @@ def matches?(actual)
end

def failure_message_for_should
"expected ##{predicate(@expected)}(#{@args[0].inspect}) to return true, got false"
"expected ##{predicate(@expected)}#{failure_message_args_description} to return true, got false"
end

def failure_message_for_should_not
"expected ##{predicate(@expected)}(#{@args[0].inspect}) to return false, got true"
"expected ##{predicate(@expected)}#{failure_message_args_description} to return false, got true"
end

def description
[method_description(@expected), args_description(@args)].compact.join(' ')
[method_description(@expected), args_description].compact.join(' ')
end

private
Expand All @@ -30,9 +30,14 @@ def method_description(method)
method.to_s.gsub('_', ' ')
end

def args_description(args)
return nil if args.empty?
args.map { |arg| arg.inspect }.join(', ')
def args_description
return nil if @args.empty?
@args.map { |arg| arg.inspect }.join(', ')
end

def failure_message_args_description
desc = args_description
"(#{desc})" if desc
end
end
end
Expand Down
34 changes: 33 additions & 1 deletion spec/rspec/matchers/has_spec.rb
Expand Up @@ -11,14 +11,30 @@
}.should fail_with("expected #has_key?(:a) to return true, got false")
end

it 'does not include any args in the failure message if no args were given to the matcher' do
o = Object.new
def o.has_some_stuff?; false; end
expect {
o.should have_some_stuff
}.to fail_with("expected #has_some_stuff? to return true, got false")
end

it 'includes multiple args in the failure message if multiple args were given to the matcher' do
o = Object.new
def o.has_some_stuff?(*_); false; end
expect {
o.should have_some_stuff(:a, 7, "foo")
}.to fail_with('expected #has_some_stuff?(:a, 7, "foo") to return true, got false')
end

it "fails if #has_sym?(*args) returns nil" do
klass = Class.new do
def has_foo?
end
end
lambda {
klass.new.should have_foo
}.should fail_with("expected #has_foo?(nil) to return true, got false")
}.should fail_with(/expected #has_foo.* to return true, got false/)
end

it "fails if target does not respond to #has_sym?" do
Expand Down Expand Up @@ -68,6 +84,22 @@ def o.has_sym?(*args)
end
lambda { o.should_not have_sym(:foo) }.should raise_error("Funky exception")
end

it 'does not include any args in the failure message if no args were given to the matcher' do
o = Object.new
def o.has_some_stuff?; true; end
expect {
o.should_not have_some_stuff
}.to fail_with("expected #has_some_stuff? to return false, got true")
end

it 'includes multiple args in the failure message if multiple args were given to the matcher' do
o = Object.new
def o.has_some_stuff?(*_); true; end
expect {
o.should_not have_some_stuff(:a, 7, "foo")
}.to fail_with('expected #has_some_stuff?(:a, 7, "foo") to return false, got true')
end
end

describe "has" do
Expand Down

0 comments on commit e92aad9

Please sign in to comment.