Skip to content

Commit 7107928

Browse files
lukemeliastas
authored andcommitted
Improved failure message and description for have_attribute matcher
1 parent 1a2a44a commit 7107928

File tree

3 files changed

+80
-7
lines changed

3 files changed

+80
-7
lines changed

lib/jsonapi/rspec/attributes.rb

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,46 @@ module Attributes
44
::RSpec::Matchers.define :have_attribute do |attr|
55
match do |actual|
66
actual = JSONAPI::RSpec.as_indifferent_hash(actual)
7-
(actual['attributes'] || {}).key?(attr.to_s) &&
8-
(!@val_set || actual['attributes'][attr.to_s] == @val)
7+
@attributes_node = actual['attributes']
8+
9+
return false unless @attributes_node
10+
11+
@has_attribute = @attributes_node.key?(attr.to_s)
12+
if @has_attribute && @should_match_value
13+
@actual_value = @attributes_node[attr.to_s]
14+
15+
# Work nicely with diffable
16+
@actual = @actual_value
17+
@expected = @expected_value
18+
19+
return @actual == @expected
20+
end
21+
@has_attribute
22+
end
23+
24+
chain :with_value do |expected_value|
25+
@should_match_value = true
26+
@expected_value = expected_value
27+
end
28+
29+
description do
30+
result = "have attribute #{attr.inspect}"
31+
if @should_match_value
32+
result << " with value #{@expected_value.inspect}"
33+
end
34+
result
935
end
1036

11-
chain :with_value do |val|
12-
@val_set = true
13-
@val = val
37+
failure_message do |actual|
38+
if @has_attribute
39+
"expected `#{attr}` attribute to have value `#{@expected}` but was `#{@actual}`"
40+
else
41+
"expected attributes to include `#{attr}`. Actual attributes were #{@attributes_node.keys}"
42+
end
1443
end
44+
45+
diffable
46+
attr_reader :actual, :expected
1547
end
1648

1749
::RSpec::Matchers.define :have_jsonapi_attributes do |*attrs|

spec/jsonapi/attributes_spec.rb

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,42 @@
66
'attributes' => {
77
'one' => 1,
88
'two' => 2,
9-
'four' => 3
9+
'four' => 3,
10+
'six' => { foo: 'bar' }
1011
}
1112
}
1213
end
1314

1415
describe '#have_attribute' do
1516
it { expect(doc).to have_attribute(:one) }
1617
it { expect(doc).not_to have_attribute(:five) }
18+
it { expect(doc).to have_attribute(:one).with_value(1) }
19+
it { expect(doc).not_to have_attribute(:one).with_value(2) }
20+
it { expect(doc).to have_attribute(:six).with_value(foo: 'bar') }
21+
22+
it 'rejects with an appropriate failure message' do
23+
expect {
24+
expect(doc).to have_attribute(:three)
25+
}.to fail_with('expected attributes to include `three`. Actual attributes were ["one", "two", "four", "six"]')
26+
end
27+
28+
it 'rejects with an appropriate failure message for chained with_value, simple values' do
29+
expect {
30+
expect(doc).to have_attribute(:one).with_value(2)
31+
}.to fail_with('expected `one` attribute to have value `2` but was `1`')
32+
end
33+
34+
it 'rejects with an appropriate failure message for chained with_value, complex values show diff' do
35+
expect {
36+
expect(doc).to have_attribute(:six).with_value(bar: 'baz')
37+
}.to fail_with(/expected `six` attribute to have value `{:bar=>"baz"}` but was `{:foo=>"bar"}`.*Diff:/m)
38+
end
1739
end
1840

1941
describe '#have_jsonapi_attributes' do
2042
it { expect(doc).to have_jsonapi_attributes(:one, :two) }
2143
it { expect(doc).not_to have_jsonapi_attributes(:two, :five) }
22-
it { expect(doc).to have_jsonapi_attributes(:one, :two, :four).exactly }
44+
it { expect(doc).to have_jsonapi_attributes(:one, :two, :four, :six).exactly }
2345
it { expect(doc).not_to have_jsonapi_attributes(:one).exactly }
2446
end
2547
end

spec/spec_helper.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,27 @@
66
end
77
SimpleCov.minimum_coverage 90
88

9+
module FailMatchers
10+
def fail
11+
raise_error(RSpec::Expectations::ExpectationNotMetError)
12+
end
13+
14+
def fail_with(message)
15+
raise_error(RSpec::Expectations::ExpectationNotMetError, message)
16+
end
17+
18+
def fail_including(snippet)
19+
raise_error(
20+
RSpec::Expectations::ExpectationNotMetError,
21+
a_string_including(snippet)
22+
)
23+
end
24+
end
25+
926
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
1027
RSpec.configure do |config|
28+
config.include FailMatchers
29+
1130
config.expect_with :rspec do |expectations|
1231
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
1332
end

0 commit comments

Comments
 (0)