-
-
Notifications
You must be signed in to change notification settings - Fork 398
/
predicates.feature
218 lines (181 loc) · 7.09 KB
/
predicates.feature
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
Feature: Predicate matchers
Ruby objects commonly provide predicate methods:
```ruby
7.zero? # => false
0.zero? # => true
[1].empty? # => false
[].empty? # => true
{ :a => 5 }.has_key?(:b) # => false
{ :b => 5 }.has_key?(:b) # => true
```
You could use a basic equality matcher to set expectations on these:
```ruby
expect(7.zero?).to eq true # fails with "expected true, got false (using ==)"
```
...but RSpec provides dynamic predicate matchers that are more readable and provide
better failure output.
For any predicate method, RSpec gives you a corresponding matcher. Simply prefix the
method with `be_` and remove the question mark. Examples:
```ruby
expect(7).not_to be_zero # calls 7.zero?
expect([]).to be_empty # calls [].empty?
expect(x).to be_multiple_of(3) # calls x.multiple_of?(3)
```
Alternately, for a predicate method that begins with `has_` like `Hash#has_key?`, RSpec allows
you to use an alternate form since `be_has_key` makes no sense.
```ruby
expect(hash).to have_key(:foo) # calls hash.has_key?(:foo)
expect(array).not_to have_odd_values # calls array.has_odd_values?
```
In either case, RSpec provides nice, clear error messages, such as:
`expected zero? to be truthy, got false`
Calling private methods will also fail:
`expected private_method? to return true but it's a private method`
Any arguments passed to the matcher will be passed on to the predicate method.
Scenario: should be_zero (based on Integer#zero?)
Given a file named "should_be_zero_spec.rb" with:
"""ruby
RSpec.describe 0 do
it { is_expected.to be_zero }
end
RSpec.describe 7 do
it { is_expected.to be_zero } # deliberate failure
end
"""
When I run `rspec should_be_zero_spec.rb`
Then the output should contain "2 examples, 1 failure"
And the output should contain "expected `7.zero?` to be truthy, got false"
Scenario: should_not be_empty (based on Array#empty?)
Given a file named "should_not_be_empty_spec.rb" with:
"""ruby
RSpec.describe [1, 2, 3] do
it { is_expected.not_to be_empty }
end
RSpec.describe [] do
it { is_expected.not_to be_empty } # deliberate failure
end
"""
When I run `rspec should_not_be_empty_spec.rb`
Then the output should contain "2 examples, 1 failure"
And the output should contain "expected `[].empty?` to be falsey, got true"
Scenario: should have_key (based on Hash#has_key?)
Given a file named "should_have_key_spec.rb" with:
"""ruby
RSpec.describe Hash do
subject { { :foo => 7 } }
it { is_expected.to have_key(:foo) }
it { is_expected.to have_key(:bar) } # deliberate failure
end
"""
When I run `rspec should_have_key_spec.rb`
Then the output should contain "2 examples, 1 failure"
And the output should contain "expected `{:foo=>7}.has_key?(:bar)` to be truthy, got false"
Scenario: should_not have_all_string_keys (based on custom #has_all_string_keys? method)
Given a file named "should_not_have_all_string_keys_spec.rb" with:
"""ruby
class Float
def has_decimals?
round != self
end
end
RSpec.describe Float do
context 'with decimals' do
subject { 4.2 }
it { is_expected.to have_decimals }
end
context 'with no decimals' do
subject { 42.0 }
it { is_expected.to have_decimals } # deliberate failure
end
end
"""
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 `42.0.has_decimals?` to be truthy, got false"
Scenario: matcher arguments are passed on to the predicate method
Given a file named "predicate_matcher_argument_spec.rb" with:
"""ruby
class Integer
def multiple_of?(x)
(self % x).zero?
end
end
RSpec.describe 12 do
it { is_expected.to be_multiple_of(3) }
it { is_expected.not_to be_multiple_of(7) }
# deliberate failures
it { is_expected.not_to be_multiple_of(4) }
it { is_expected.to be_multiple_of(5) }
end
"""
When I run `rspec predicate_matcher_argument_spec.rb`
Then the output should contain "4 examples, 2 failures"
And the output should contain "expected `12.multiple_of?(4)` to be falsey, got true"
And the output should contain "expected `12.multiple_of?(5)` to be truthy, got false"
Scenario: the config `strict_predicate_matchers` impacts matching of results other than `true` and `false`
Given a file named "strict_or_not.rb" with:
"""ruby
class StrangeResult
def has_strange_result?
42
end
end
RSpec.describe StrangeResult do
subject { StrangeResult.new }
before do
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.strict_predicate_matchers = strict
end
end
end
context 'with non-strict matchers (default)' do
let(:strict) { false }
it { is_expected.to have_strange_result }
end
context 'with strict matchers' do
let(:strict) { true }
# deliberate failure
it { is_expected.to have_strange_result }
end
end
"""
When I run `rspec strict_or_not.rb`
Then the output should contain "2 examples, 1 failure"
And the output should contain "has_strange_result?` to return true, got 42"
Scenario: calling private method with be_predicate causes error
Given a file named "attempting_to_match_private_method_spec.rb" with:
"""ruby
class WithPrivateMethods
def secret?
true
end
private :secret?
end
RSpec.describe 'private methods' do
subject { WithPrivateMethods.new }
# deliberate failure
it { is_expected.to be_secret }
end
"""
When I run `rspec attempting_to_match_private_method_spec.rb`
Then the output should contain "1 example, 1 failure"
And the output should contain "`secret?` is a private method"
Scenario: calling private method with have_predicate causes error
Given a file named "attempting_to_match_private_method_spec.rb" with:
"""ruby
class WithPrivateMethods
def has_secret?
true
end
private :has_secret?
end
RSpec.describe 'private methods' do
subject { WithPrivateMethods.new }
# deliberate failure
it { is_expected.to have_secret }
end
"""
When I run `rspec attempting_to_match_private_method_spec.rb`
Then the output should contain "1 example, 1 failure"
And the output should contain "`has_secret?` is a private method"