Skip to content

Commit 773da4a

Browse files
authored
Add ability to inspect all describable matchers (#219)
In a previous commit, a new inspection tree builder was added which was designed to handle built-in RSpec matchers as well as matchers created via the matcher DSL, by reusing the `description` of the matcher as the inspection text. However, this inspector did not cover custom matchers defined from scratch rather than via the DSL. This commit corrects this by making use of the `is_a_describable_matcher?` method from RSpec::Matchers.
1 parent 5700196 commit 773da4a

File tree

6 files changed

+218
-94
lines changed

6 files changed

+218
-94
lines changed

lib/super_diff/rspec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def self.rspec_version
113113
ObjectInspection::InspectionTreeBuilders::ObjectHavingAttributes,
114114
# ObjectInspection::InspectionTreeBuilders::Primitive,
115115
ObjectInspection::InspectionTreeBuilders::ValueWithin,
116-
ObjectInspection::InspectionTreeBuilders::RSpecMatcher
116+
ObjectInspection::InspectionTreeBuilders::GenericDescribableMatcher
117117
)
118118
end
119119
end

lib/super_diff/rspec/object_inspection/inspection_tree_builders.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ module InspectionTreeBuilders
1414
:Double,
1515
"super_diff/rspec/object_inspection/inspection_tree_builders/double"
1616
)
17+
autoload(
18+
:GenericDescribableMatcher,
19+
"super_diff/rspec/object_inspection/inspection_tree_builders/generic_describable_matcher"
20+
)
1721
autoload(
1822
:HashIncluding,
1923
"super_diff/rspec/object_inspection/inspection_tree_builders/hash_including"
@@ -34,10 +38,6 @@ module InspectionTreeBuilders
3438
:Primitive,
3539
"super_diff/rspec/object_inspection/inspection_tree_builders/primitive"
3640
)
37-
autoload(
38-
:RSpecMatcher,
39-
"super_diff/rspec/object_inspection/inspection_tree_builders/rspec_matcher"
40-
)
4141
autoload(
4242
:ValueWithin,
4343
"super_diff/rspec/object_inspection/inspection_tree_builders/value_within"

lib/super_diff/rspec/object_inspection/inspection_tree_builders/rspec_matcher.rb renamed to lib/super_diff/rspec/object_inspection/inspection_tree_builders/generic_describable_matcher.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ module SuperDiff
22
module RSpec
33
module ObjectInspection
44
module InspectionTreeBuilders
5-
class RSpecMatcher < SuperDiff::ObjectInspection::InspectionTreeBuilders::Base
5+
class GenericDescribableMatcher < SuperDiff::ObjectInspection::InspectionTreeBuilders::Base
66
def self.applies_to?(value)
7-
value.is_a?(::RSpec::Matchers::BuiltIn::BaseMatcher) ||
8-
value.is_a?(::RSpec::Matchers::DSL::Matcher)
7+
::RSpec::Matchers.is_a_describable_matcher?(value)
98
end
109

1110
def call
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
require "spec_helper"
2+
3+
RSpec.describe "Integration with describable matchers not handled specially",
4+
type: :integration do
5+
context "when the expected value contains a built-in matcher (not an aliased matcher)" do
6+
it "produces the correct failure message when used in the positive" do
7+
as_both_colored_and_uncolored do |color_enabled|
8+
snippet = <<~TEST.strip
9+
actual = {
10+
number: "not a number"
11+
}
12+
expected = hash_including(
13+
number: be_a(Numeric)
14+
)
15+
expect(actual).to match(expected)
16+
TEST
17+
program = make_plain_test_program(snippet, color_enabled: color_enabled)
18+
19+
expected_output =
20+
build_expected_output(
21+
color_enabled: color_enabled,
22+
snippet: "expect(actual).to match(expected)",
23+
expectation:
24+
proc do
25+
line do
26+
plain "Expected "
27+
actual %|{ number: "not a number" }|
28+
plain " to match "
29+
expected "#<a hash including (number: #<be a kind of Numeric>)>"
30+
plain "."
31+
end
32+
end,
33+
diff:
34+
proc do
35+
plain_line " {"
36+
expected_line "- number: #<be a kind of Numeric>"
37+
actual_line %|+ number: "not a number"|
38+
plain_line " }"
39+
end
40+
)
41+
42+
expect(program).to produce_output_when_run(expected_output).in_color(
43+
color_enabled
44+
)
45+
end
46+
end
47+
48+
it "produces the correct failure message when used in the negative" do
49+
as_both_colored_and_uncolored do |color_enabled|
50+
snippet = <<~TEST.strip
51+
actual = {
52+
number: 4
53+
}
54+
expected = hash_including(
55+
number: be_a(Numeric)
56+
)
57+
expect(actual).not_to match(expected)
58+
TEST
59+
program = make_plain_test_program(snippet, color_enabled: color_enabled)
60+
61+
expected_output =
62+
build_expected_output(
63+
color_enabled: color_enabled,
64+
snippet: "expect(actual).not_to match(expected)",
65+
expectation:
66+
proc do
67+
line do
68+
plain "Expected "
69+
actual "{ number: 4 }"
70+
plain " not to match "
71+
expected "#<a hash including (number: #<be a kind of Numeric>)>"
72+
plain "."
73+
end
74+
end
75+
)
76+
77+
expect(program).to produce_output_when_run(expected_output).in_color(
78+
color_enabled
79+
)
80+
end
81+
end
82+
end
83+
84+
context "when the expected value contains a custom matcher defined via RSpec::Matchers::DSL" do
85+
it "produces the correct failure message" do
86+
as_both_colored_and_uncolored do |color_enabled|
87+
snippet = <<~TEST.strip
88+
actual = {
89+
number: "something else"
90+
}
91+
expected = hash_including(
92+
number: failing_custom_matcher_from_dsl(42)
93+
)
94+
expect(actual).to match(expected)
95+
TEST
96+
program = make_plain_test_program(snippet, color_enabled: color_enabled)
97+
98+
expected_output =
99+
build_expected_output(
100+
color_enabled: color_enabled,
101+
snippet: "expect(actual).to match(expected)",
102+
newline_before_expectation: true,
103+
expectation:
104+
proc do
105+
line do
106+
plain "Expected "
107+
actual %|{ number: "something else" }|
108+
end
109+
110+
line do
111+
plain "to match "
112+
expected "#<a hash including (number: #<custom matcher defined via the DSL with value 42>)>"
113+
end
114+
end,
115+
diff:
116+
proc do
117+
plain_line " {"
118+
expected_line "- number: #<custom matcher defined via the DSL with value 42>"
119+
actual_line %|+ number: "something else"|
120+
plain_line " }"
121+
end
122+
)
123+
124+
expect(program).to produce_output_when_run(expected_output).in_color(
125+
color_enabled
126+
)
127+
end
128+
end
129+
end
130+
131+
context "when the expected value contains a custom matcher defined from scratch" do
132+
it "produces the correct failure message" do
133+
as_both_colored_and_uncolored do |color_enabled|
134+
snippet = <<~TEST.strip
135+
actual = {
136+
number: "something else"
137+
}
138+
expected = hash_including(
139+
number: failing_custom_matcher_from_scratch(42)
140+
)
141+
expect(actual).to match(expected)
142+
TEST
143+
program = make_plain_test_program(snippet, color_enabled: color_enabled)
144+
145+
expected_output =
146+
build_expected_output(
147+
color_enabled: color_enabled,
148+
snippet: "expect(actual).to match(expected)",
149+
newline_before_expectation: true,
150+
expectation:
151+
proc do
152+
line do
153+
plain "Expected "
154+
actual %|{ number: "something else" }|
155+
end
156+
157+
line do
158+
plain "to match "
159+
expected "#<a hash including (number: #<custom matcher defined from scratch with value 42>)>"
160+
end
161+
end,
162+
diff:
163+
proc do
164+
plain_line " {"
165+
expected_line "- number: #<custom matcher defined from scratch with value 42>"
166+
actual_line %|+ number: "something else"|
167+
plain_line " }"
168+
end
169+
)
170+
171+
expect(program).to produce_output_when_run(expected_output).in_color(
172+
color_enabled
173+
)
174+
end
175+
end
176+
end
177+
end

spec/integration/rspec/unhandled_matcher_spec.rb

Lines changed: 0 additions & 86 deletions
This file was deleted.

spec/support/integration/matchers.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,40 @@ def pass_with_singleline_failure_message
3232
PassWithSinglelineFailureMessageMatcher.new
3333
end
3434

35+
RSpec::Matchers.define :failing_custom_matcher_from_dsl do |value|
36+
match { false }
37+
38+
description do
39+
"custom matcher defined via the DSL with value #{value.inspect}"
40+
end
41+
end
42+
43+
def failing_custom_matcher_from_scratch(value)
44+
FailingCustomMatcherFromScratch.new(value)
45+
end
46+
47+
class FailingCustomMatcherFromScratch
48+
def initialize(value)
49+
@value = value
50+
end
51+
52+
def matches?(_)
53+
false
54+
end
55+
56+
def description
57+
"custom matcher defined from scratch with value #{value.inspect}"
58+
end
59+
60+
def failure_message
61+
"Expected custom matcher not to fail, but did"
62+
end
63+
64+
private
65+
66+
attr_reader :value
67+
end
68+
3569
class FailWithIndentedMultilineFailureMessageMatcher
3670
def matches?(_)
3771
false

0 commit comments

Comments
 (0)