rspec-expectations 3.0.0.beta1 → 3.0.0.beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +2 -2
- data/.yardopts +1 -0
- data/Changelog.md +138 -0
- data/README.md +75 -8
- data/features/README.md +2 -2
- data/features/built_in_matchers/README.md +12 -9
- data/features/built_in_matchers/comparisons.feature +2 -2
- data/features/built_in_matchers/contain_exactly.feature +46 -0
- data/features/built_in_matchers/expect_change.feature +2 -2
- data/features/built_in_matchers/include.feature +0 -48
- data/features/built_in_matchers/output.feature +70 -0
- data/features/composing_matchers.feature +250 -0
- data/features/compound_expectations.feature +45 -0
- data/features/custom_matchers/access_running_example.feature +1 -1
- data/features/custom_matchers/define_matcher.feature +6 -6
- data/features/custom_matchers/define_matcher_outside_rspec.feature +4 -8
- data/features/test_frameworks/{test_unit.feature → minitest.feature} +11 -11
- data/lib/rspec/expectations.rb +31 -42
- data/lib/rspec/expectations/diff_presenter.rb +141 -0
- data/lib/rspec/expectations/differ.rb +22 -132
- data/lib/rspec/expectations/encoded_string.rb +56 -0
- data/lib/rspec/expectations/expectation_target.rb +0 -30
- data/lib/rspec/expectations/fail_with.rb +2 -2
- data/lib/rspec/expectations/handler.rb +128 -31
- data/lib/rspec/expectations/minitest_integration.rb +16 -0
- data/lib/rspec/expectations/syntax.rb +4 -58
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/matchers.rb +298 -60
- data/lib/rspec/matchers/aliased_matcher.rb +35 -0
- data/lib/rspec/matchers/built_in.rb +37 -33
- data/lib/rspec/matchers/built_in/base_matcher.rb +25 -15
- data/lib/rspec/matchers/built_in/be.rb +23 -31
- data/lib/rspec/matchers/built_in/be_between.rb +55 -0
- data/lib/rspec/matchers/built_in/be_within.rb +15 -11
- data/lib/rspec/matchers/built_in/change.rb +198 -81
- data/lib/rspec/matchers/built_in/compound.rb +106 -0
- data/lib/rspec/matchers/built_in/contain_exactly.rb +245 -0
- data/lib/rspec/matchers/built_in/eq.rb +43 -4
- data/lib/rspec/matchers/built_in/eql.rb +2 -2
- data/lib/rspec/matchers/built_in/equal.rb +35 -18
- data/lib/rspec/matchers/built_in/has.rb +16 -15
- data/lib/rspec/matchers/built_in/include.rb +45 -23
- data/lib/rspec/matchers/built_in/match.rb +6 -3
- data/lib/rspec/matchers/built_in/operators.rb +103 -0
- data/lib/rspec/matchers/built_in/output.rb +108 -0
- data/lib/rspec/matchers/built_in/raise_error.rb +9 -15
- data/lib/rspec/matchers/built_in/respond_to.rb +5 -4
- data/lib/rspec/matchers/built_in/satisfy.rb +4 -3
- data/lib/rspec/matchers/built_in/start_and_end_with.rb +37 -16
- data/lib/rspec/matchers/built_in/throw_symbol.rb +6 -5
- data/lib/rspec/matchers/built_in/yield.rb +31 -29
- data/lib/rspec/matchers/composable.rb +138 -0
- data/lib/rspec/matchers/dsl.rb +330 -0
- data/lib/rspec/matchers/generated_descriptions.rb +6 -6
- data/lib/rspec/matchers/matcher_delegator.rb +33 -0
- data/lib/rspec/matchers/pretty.rb +13 -2
- data/spec/rspec/expectations/{differ_spec.rb → diff_presenter_spec.rb} +56 -36
- data/spec/rspec/expectations/encoded_string_spec.rb +74 -0
- data/spec/rspec/expectations/extensions/kernel_spec.rb +11 -11
- data/spec/rspec/expectations/fail_with_spec.rb +8 -8
- data/spec/rspec/expectations/handler_spec.rb +27 -49
- data/spec/rspec/expectations/minitest_integration_spec.rb +27 -0
- data/spec/rspec/expectations/syntax_spec.rb +17 -67
- data/spec/rspec/expectations_spec.rb +7 -52
- data/spec/rspec/matchers/aliased_matcher_spec.rb +48 -0
- data/spec/rspec/matchers/aliases_spec.rb +449 -0
- data/spec/rspec/matchers/{base_matcher_spec.rb → built_in/base_matcher_spec.rb} +24 -3
- data/spec/rspec/matchers/built_in/be_between_spec.rb +159 -0
- data/spec/rspec/matchers/{be_instance_of_spec.rb → built_in/be_instance_of_spec.rb} +0 -0
- data/spec/rspec/matchers/{be_kind_of_spec.rb → built_in/be_kind_of_spec.rb} +0 -0
- data/spec/rspec/matchers/{be_spec.rb → built_in/be_spec.rb} +76 -32
- data/spec/rspec/matchers/{be_within_spec.rb → built_in/be_within_spec.rb} +6 -2
- data/spec/rspec/matchers/{change_spec.rb → built_in/change_spec.rb} +310 -69
- data/spec/rspec/matchers/built_in/compound_spec.rb +292 -0
- data/spec/rspec/matchers/built_in/contain_exactly_spec.rb +441 -0
- data/spec/rspec/matchers/{cover_spec.rb → built_in/cover_spec.rb} +0 -0
- data/spec/rspec/matchers/built_in/eq_spec.rb +156 -0
- data/spec/rspec/matchers/{eql_spec.rb → built_in/eql_spec.rb} +2 -2
- data/spec/rspec/matchers/built_in/equal_spec.rb +106 -0
- data/spec/rspec/matchers/{exist_spec.rb → built_in/exist_spec.rb} +1 -1
- data/spec/rspec/matchers/{has_spec.rb → built_in/has_spec.rb} +39 -0
- data/spec/rspec/matchers/{include_spec.rb → built_in/include_spec.rb} +118 -109
- data/spec/rspec/matchers/{match_spec.rb → built_in/match_spec.rb} +30 -2
- data/spec/rspec/matchers/{operator_matcher_spec.rb → built_in/operators_spec.rb} +26 -26
- data/spec/rspec/matchers/built_in/output_spec.rb +165 -0
- data/spec/rspec/matchers/{raise_error_spec.rb → built_in/raise_error_spec.rb} +81 -11
- data/spec/rspec/matchers/{respond_to_spec.rb → built_in/respond_to_spec.rb} +0 -0
- data/spec/rspec/matchers/{satisfy_spec.rb → built_in/satisfy_spec.rb} +0 -0
- data/spec/rspec/matchers/{start_with_end_with_spec.rb → built_in/start_and_end_with_spec.rb} +82 -15
- data/spec/rspec/matchers/{throw_symbol_spec.rb → built_in/throw_symbol_spec.rb} +29 -10
- data/spec/rspec/matchers/{yield_spec.rb → built_in/yield_spec.rb} +90 -0
- data/spec/rspec/matchers/configuration_spec.rb +7 -39
- data/spec/rspec/matchers/description_generation_spec.rb +22 -6
- data/spec/rspec/matchers/dsl_spec.rb +838 -0
- data/spec/rspec/matchers/legacy_spec.rb +101 -0
- data/spec/rspec/matchers_spec.rb +74 -0
- data/spec/spec_helper.rb +35 -21
- data/spec/support/shared_examples.rb +26 -4
- metadata +172 -116
- metadata.gz.sig +3 -4
- checksums.yaml +0 -15
- checksums.yaml.gz.sig +0 -0
- data/features/built_in_matchers/match_array.feature +0 -37
- data/lib/rspec/expectations/errors.rb +0 -9
- data/lib/rspec/expectations/extensions.rb +0 -1
- data/lib/rspec/expectations/extensions/object.rb +0 -29
- data/lib/rspec/matchers/built_in/match_array.rb +0 -51
- data/lib/rspec/matchers/compatibility.rb +0 -14
- data/lib/rspec/matchers/matcher.rb +0 -301
- data/lib/rspec/matchers/method_missing.rb +0 -12
- data/lib/rspec/matchers/operator_matcher.rb +0 -99
- data/lib/rspec/matchers/test_unit_integration.rb +0 -11
- data/spec/rspec/matchers/eq_spec.rb +0 -60
- data/spec/rspec/matchers/equal_spec.rb +0 -78
- data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
- data/spec/rspec/matchers/match_array_spec.rb +0 -194
- data/spec/rspec/matchers/matcher_spec.rb +0 -706
- data/spec/rspec/matchers/matchers_spec.rb +0 -36
- data/spec/rspec/matchers/method_missing_spec.rb +0 -28
- data/spec/support/classes.rb +0 -56
- data/spec/support/in_sub_process.rb +0 -37
- data/spec/support/ruby_version.rb +0 -10
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe "a matcher defined using the matcher DSL" do
|
@@ -55,3 +56,840 @@ describe "a matcher defined using the matcher DSL" do
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
end
|
59
|
+
|
60
|
+
class UnexpectedError < StandardError; end
|
61
|
+
module MatcherHelperModule
|
62
|
+
def self.included(base)
|
63
|
+
base.module_exec do
|
64
|
+
def included_method; end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.extended(base)
|
69
|
+
base.instance_exec do
|
70
|
+
def extended_method; end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def greeting
|
75
|
+
"Hello, World"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
module RSpec::Matchers::DSL
|
80
|
+
describe Matcher do
|
81
|
+
def new_matcher(name, *expected, &block)
|
82
|
+
RSpec::Matchers::DSL::Matcher.
|
83
|
+
new(name, block, *expected).
|
84
|
+
tap { |m| m.matcher_execution_context = self }
|
85
|
+
end
|
86
|
+
|
87
|
+
it_behaves_like "an RSpec matcher", :valid_value => 1, :invalid_value => 2 do
|
88
|
+
let(:matcher) do
|
89
|
+
new_matcher(:equal_to_1) do
|
90
|
+
match { |v| v == 1 }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "can be stored aside and used later" do
|
96
|
+
# Supports using rspec-expectation matchers as argument matchers in
|
97
|
+
# rspec-mocks.
|
98
|
+
RSpec::Matchers.define :example_matcher do |expected|
|
99
|
+
match do |actual|
|
100
|
+
actual == expected
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
m1 = example_matcher(1)
|
105
|
+
m2 = example_matcher(2)
|
106
|
+
|
107
|
+
expect(m1.matches?(1)).to be_truthy
|
108
|
+
expect(m2.matches?(2)).to be_truthy
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'using deprecated APIs' do
|
112
|
+
before { allow_deprecation }
|
113
|
+
|
114
|
+
describe "failure_message_for_should" do
|
115
|
+
let(:matcher) do
|
116
|
+
new_matcher(:foo) do
|
117
|
+
match { false }
|
118
|
+
failure_message_for_should { "failed" }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
line = __LINE__ - 3
|
122
|
+
|
123
|
+
it 'defines the failure message for a positive expectation' do
|
124
|
+
expect {
|
125
|
+
expect(nil).to matcher
|
126
|
+
}.to fail_with("failed")
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'prints a deprecation warning' do
|
130
|
+
expect_deprecation_with_call_site(__FILE__, line, /failure_message_for_should/)
|
131
|
+
matcher
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "failure_message_for_should_not" do
|
136
|
+
let(:matcher) do
|
137
|
+
new_matcher(:foo) do
|
138
|
+
match { true }
|
139
|
+
failure_message_for_should_not { "failed" }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
line = __LINE__ - 3
|
143
|
+
|
144
|
+
it 'defines the failure message for a negative expectation' do
|
145
|
+
expect {
|
146
|
+
expect(nil).not_to matcher
|
147
|
+
}.to fail_with("failed")
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'prints a deprecation warning' do
|
151
|
+
expect_deprecation_with_call_site(__FILE__, line, /failure_message_for_should_not/)
|
152
|
+
matcher
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "match_for_should" do
|
157
|
+
let(:matcher) do
|
158
|
+
new_matcher(:foo) do
|
159
|
+
match_for_should { |arg| arg }
|
160
|
+
end
|
161
|
+
end
|
162
|
+
line = __LINE__ - 3
|
163
|
+
|
164
|
+
it 'defines the positive expectation match logic' do
|
165
|
+
expect(true).to matcher
|
166
|
+
expect { expect(false).to matcher }.to fail_with(/foo/)
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'prints a deprecation warning' do
|
170
|
+
expect_deprecation_with_call_site(__FILE__, line, /match_for_should/)
|
171
|
+
matcher
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "match_for_should_not" do
|
176
|
+
let(:matcher) do
|
177
|
+
new_matcher(:foo) do
|
178
|
+
match_for_should_not { |arg| !arg }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
line = __LINE__ - 3
|
182
|
+
|
183
|
+
it 'defines the positive expectation match logic' do
|
184
|
+
expect(false).not_to matcher
|
185
|
+
expect { expect(true).not_to matcher }.to fail_with(/foo/)
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'prints a deprecation warning' do
|
189
|
+
expect_deprecation_with_call_site(__FILE__, line, /match_for_should_not/)
|
190
|
+
matcher
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context "with an included module" do
|
196
|
+
let(:matcher) do
|
197
|
+
new_matcher(:be_a_greeting) do
|
198
|
+
include MatcherHelperModule
|
199
|
+
match { |actual| actual == greeting }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
it "has access to the module's methods" do
|
204
|
+
matcher.matches?("Hello, World")
|
205
|
+
end
|
206
|
+
|
207
|
+
it "runs the module's included hook" do
|
208
|
+
expect(matcher).to respond_to(:included_method)
|
209
|
+
end
|
210
|
+
|
211
|
+
it "does not run the module's extended hook" do
|
212
|
+
expect(matcher).not_to respond_to(:extended_method)
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'allows multiple modules to be included at once' do
|
216
|
+
m = new_matcher(:multiple_modules) do
|
217
|
+
include Enumerable, Comparable
|
218
|
+
end
|
219
|
+
expect(m).to be_a(Enumerable)
|
220
|
+
expect(m).to be_a(Comparable)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context "without overrides" do
|
225
|
+
let(:matcher) do
|
226
|
+
new_matcher(:be_a_multiple_of, 3) do |multiple|
|
227
|
+
match do |actual|
|
228
|
+
actual % multiple == 0
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
it "provides a default description" do
|
234
|
+
expect(matcher.description).to eq "be a multiple of 3"
|
235
|
+
end
|
236
|
+
|
237
|
+
it "provides a default positive expectation failure message" do
|
238
|
+
matcher.matches?(8)
|
239
|
+
expect(matcher.failure_message).to eq "expected 8 to be a multiple of 3"
|
240
|
+
end
|
241
|
+
|
242
|
+
it "provides a default negative expectation failure message" do
|
243
|
+
matcher.matches?(9)
|
244
|
+
expect(matcher.failure_message_when_negated).to eq "expected 9 not to be a multiple of 3"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
context "with separate match logic for positive and negative expectations" do
|
249
|
+
let(:matcher) do
|
250
|
+
new_matcher(:to_be_composed_of, 7, 11) do |a, b|
|
251
|
+
match do |actual|
|
252
|
+
actual == a * b
|
253
|
+
end
|
254
|
+
|
255
|
+
match_when_negated do |actual|
|
256
|
+
actual == a + b
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
it "invokes the match block for #matches?" do
|
262
|
+
expect(matcher.matches?(77)).to be_truthy
|
263
|
+
expect(matcher.matches?(18)).to be_falsey
|
264
|
+
end
|
265
|
+
|
266
|
+
it "invokes the match_when_negated block for #does_not_match?" do
|
267
|
+
expect(matcher.does_not_match?(77)).to be_falsey
|
268
|
+
expect(matcher.does_not_match?(18)).to be_truthy
|
269
|
+
end
|
270
|
+
|
271
|
+
it "provides a default failure message for negative expectations" do
|
272
|
+
matcher.does_not_match?(77)
|
273
|
+
expect(matcher.failure_message_when_negated).to eq "expected 77 not to to be composed of 7 and 11"
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'can access helper methods from `match_when_negated`' do
|
277
|
+
matcher = new_matcher(:be_foo) do
|
278
|
+
def foo
|
279
|
+
:foo
|
280
|
+
end
|
281
|
+
|
282
|
+
match_when_negated do |actual|
|
283
|
+
actual != foo
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
expect(matcher.does_not_match?(:bar)).to be true
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
it "allows helper methods to be defined with #define_method to have access to matcher parameters" do
|
292
|
+
matcher = new_matcher(:name, 3, 4) do |a, b|
|
293
|
+
define_method(:sum) { a + b }
|
294
|
+
end
|
295
|
+
|
296
|
+
expect(matcher.sum).to eq 7
|
297
|
+
end
|
298
|
+
|
299
|
+
it "is not diffable by default" do
|
300
|
+
matcher = new_matcher(:name) { }
|
301
|
+
expect(matcher).not_to be_diffable
|
302
|
+
end
|
303
|
+
|
304
|
+
it "is diffable when told to be" do
|
305
|
+
matcher = new_matcher(:name) { diffable }
|
306
|
+
expect(matcher).to be_diffable
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'handles multiline string diffs' do
|
310
|
+
actual = "LINE1\nline2\n"
|
311
|
+
expected = "line1\nline2\n"
|
312
|
+
|
313
|
+
matcher = new_matcher(:custom_match, expected) do
|
314
|
+
match { |actual| actual == expected }
|
315
|
+
diffable
|
316
|
+
end
|
317
|
+
|
318
|
+
diff = nil
|
319
|
+
begin
|
320
|
+
allow(RSpec::Matchers.configuration).to receive(:color?).and_return(false)
|
321
|
+
expect(actual).to matcher
|
322
|
+
rescue RSpec::Expectations::ExpectationNotMetError => e
|
323
|
+
diff = e.message.sub(/\A.*Diff:/m, "Diff:").gsub(/^\s*/,'')
|
324
|
+
end
|
325
|
+
|
326
|
+
expect(diff).to eq "Diff:\n@@ -1,3 +1,3 @@\n-line1\n+LINE1\nline2\n"
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'does not confuse the diffability of different matchers' do
|
330
|
+
# Necessary to guard against a regression that involved
|
331
|
+
# using a class variable to store the diffable state,
|
332
|
+
# which had the side effect of causing all custom matchers
|
333
|
+
# to share that state
|
334
|
+
m1 = new_matcher(:m1) { diffable }
|
335
|
+
m2 = new_matcher(:m2) { }
|
336
|
+
m3 = new_matcher(:m3) { diffable }
|
337
|
+
|
338
|
+
expect(m1).to be_diffable
|
339
|
+
expect(m2).not_to be_diffable
|
340
|
+
expect(m3).to be_diffable
|
341
|
+
end
|
342
|
+
|
343
|
+
it "provides expected" do
|
344
|
+
matcher = new_matcher(:name, "expected string") { }
|
345
|
+
expect(matcher.expected).to eq 'expected string'
|
346
|
+
end
|
347
|
+
|
348
|
+
it "provides expected when there is more than one argument" do
|
349
|
+
matcher = new_matcher(:name, "expected string", "another arg") { }
|
350
|
+
expect(matcher.expected).to eq ['expected string', "another arg"]
|
351
|
+
end
|
352
|
+
|
353
|
+
it "provides expected_as_array which returns an array regardless of expected" do
|
354
|
+
matcher = new_matcher(:name, "expected string") { }
|
355
|
+
expect(matcher.expected_as_array).to eq ['expected string']
|
356
|
+
matcher = new_matcher(:name, "expected\nstring") { }
|
357
|
+
expect(matcher.expected_as_array).to eq ["expected\nstring"]
|
358
|
+
matcher = new_matcher(:name, "expected string", "another arg") { }
|
359
|
+
expect(matcher.expected_as_array).to eq ['expected string', "another arg"]
|
360
|
+
end
|
361
|
+
|
362
|
+
it "provides actual when `match` is used" do
|
363
|
+
matcher = new_matcher(:name, 'expected string') do
|
364
|
+
match {|actual|}
|
365
|
+
end
|
366
|
+
|
367
|
+
matcher.matches?('actual string')
|
368
|
+
|
369
|
+
expect(matcher.actual).to eq 'actual string'
|
370
|
+
end
|
371
|
+
|
372
|
+
it "provides actual when the `match` block accepts splat args" do
|
373
|
+
matcher = new_matcher(:actual) do
|
374
|
+
match { |*actual| actual == [5] }
|
375
|
+
end
|
376
|
+
|
377
|
+
expect(matcher.matches?(5)).to be true
|
378
|
+
expect(matcher.matches?(4)).to be false
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'allows an early `return` to be used from a `match` block' do
|
382
|
+
matcher = new_matcher(:with_return, 5) do |expected|
|
383
|
+
match { |actual| return true if expected == actual }
|
384
|
+
end
|
385
|
+
|
386
|
+
expect(matcher.matches?(5)).to be true
|
387
|
+
expect(matcher.matches?(4)).to be_falsey
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'provides actual when `match_unless_raises` is used' do
|
391
|
+
matcher = new_matcher(:name, 'expected string') do
|
392
|
+
match_unless_raises(SyntaxError) {|actual|}
|
393
|
+
end
|
394
|
+
|
395
|
+
matcher.matches?('actual string')
|
396
|
+
|
397
|
+
expect(matcher.actual).to eq 'actual string'
|
398
|
+
end
|
399
|
+
|
400
|
+
it 'allows an early `return` to be used from a `match_unless_raises` block' do
|
401
|
+
matcher = new_matcher(:with_return) do
|
402
|
+
match_unless_raises(ArgumentError) do |actual|
|
403
|
+
return actual if [true, false].include?(actual)
|
404
|
+
raise ArgumentError
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
expect(matcher.matches?(true)).to be true
|
409
|
+
# It should match even if it returns false, because no error was raised.
|
410
|
+
expect(matcher.matches?(false)).to be true
|
411
|
+
expect(matcher.matches?(4)).to be_falsey
|
412
|
+
end
|
413
|
+
|
414
|
+
it 'provides actual when `match_when_negated` is used' do
|
415
|
+
matcher = new_matcher(:name, 'expected string') do
|
416
|
+
match_when_negated {|actual|}
|
417
|
+
end
|
418
|
+
|
419
|
+
matcher.does_not_match?('actual string')
|
420
|
+
|
421
|
+
expect(matcher.actual).to eq 'actual string'
|
422
|
+
end
|
423
|
+
|
424
|
+
it 'allows an early `return` to be used from a `match_when_negated` block' do
|
425
|
+
matcher = new_matcher(:with_return, 5) do |expected|
|
426
|
+
match_when_negated { |actual| return true if expected != actual }
|
427
|
+
end
|
428
|
+
|
429
|
+
expect(matcher.does_not_match?(5)).to be_falsey
|
430
|
+
expect(matcher.does_not_match?(4)).to be true
|
431
|
+
end
|
432
|
+
|
433
|
+
context "wrapping another expectation (expect(...).to eq ...)" do
|
434
|
+
let(:matcher) do
|
435
|
+
new_matcher(:name, "value") do |expected|
|
436
|
+
match do |actual|
|
437
|
+
expect(actual).to eq expected
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
it "returns true if the wrapped expectation passes" do
|
443
|
+
expect(matcher.matches?('value')).to be_truthy
|
444
|
+
end
|
445
|
+
|
446
|
+
it "returns false if the wrapped expectation fails" do
|
447
|
+
expect(matcher.matches?('other value')).to be_falsey
|
448
|
+
end
|
449
|
+
|
450
|
+
it "can use the `include` matcher from a `match` block" do
|
451
|
+
RSpec::Matchers.define(:descend_from) do |mod|
|
452
|
+
match do |klass|
|
453
|
+
expect(klass.ancestors).to include(mod)
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
expect(Fixnum).to descend_from(Object)
|
458
|
+
expect(Fixnum).not_to descend_from(Array)
|
459
|
+
|
460
|
+
expect {
|
461
|
+
expect(Fixnum).to descend_from(Array)
|
462
|
+
}.to fail_with(/expected Fixnum to descend from Array/)
|
463
|
+
|
464
|
+
expect {
|
465
|
+
expect(Fixnum).not_to descend_from(Object)
|
466
|
+
}.to fail_with(/expected Fixnum not to descend from Object/)
|
467
|
+
end
|
468
|
+
|
469
|
+
it "can use the `match` matcher from a `match` block" do
|
470
|
+
RSpec::Matchers.define(:be_a_phone_number_string) do
|
471
|
+
match do |string|
|
472
|
+
expect(string).to match(/\A\d{3}\-\d{3}\-\d{4}\z/)
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
expect("206-123-1234").to be_a_phone_number_string
|
477
|
+
expect("foo").not_to be_a_phone_number_string
|
478
|
+
|
479
|
+
expect {
|
480
|
+
expect("foo").to be_a_phone_number_string
|
481
|
+
}.to fail_with(/expected "foo" to be a phone number string/)
|
482
|
+
|
483
|
+
expect {
|
484
|
+
expect("206-123-1234").not_to be_a_phone_number_string
|
485
|
+
}.to fail_with(/expected "206-123-1234" not to be a phone number string/)
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
context "with overrides" do
|
490
|
+
let(:matcher) do
|
491
|
+
new_matcher(:be_boolean, true) do |boolean|
|
492
|
+
match do |actual|
|
493
|
+
actual
|
494
|
+
end
|
495
|
+
description do |actual|
|
496
|
+
"be the boolean #{boolean} (actual was #{actual})"
|
497
|
+
end
|
498
|
+
failure_message do |actual|
|
499
|
+
"expected #{actual} to be the boolean #{boolean}"
|
500
|
+
end
|
501
|
+
failure_message_when_negated do |actual|
|
502
|
+
"expected #{actual} not to be the boolean #{boolean}"
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
it "does not hide result of match block when true" do
|
508
|
+
expect(matcher.matches?(true)).to be_truthy
|
509
|
+
end
|
510
|
+
|
511
|
+
it "does not hide result of match block when false" do
|
512
|
+
expect(matcher.matches?(false)).to be_falsey
|
513
|
+
end
|
514
|
+
|
515
|
+
it "overrides the description (which yields `actual`)" do
|
516
|
+
matcher.matches?(true)
|
517
|
+
expect(matcher.description).to eq "be the boolean true (actual was true)"
|
518
|
+
end
|
519
|
+
|
520
|
+
it "overrides the failure message for positive expectations" do
|
521
|
+
matcher.matches?(false)
|
522
|
+
expect(matcher.failure_message).to eq "expected false to be the boolean true"
|
523
|
+
end
|
524
|
+
|
525
|
+
it "overrides the failure message for negative expectations" do
|
526
|
+
matcher.matches?(true)
|
527
|
+
expect(matcher.failure_message_when_negated).to eq "expected true not to be the boolean true"
|
528
|
+
end
|
529
|
+
|
530
|
+
it 'can access helper methods from `description`' do
|
531
|
+
matcher = new_matcher(:desc) do
|
532
|
+
def subdesc() "sub description" end
|
533
|
+
description { "Desc (#{subdesc})" }
|
534
|
+
end
|
535
|
+
|
536
|
+
expect(matcher.description).to eq("Desc (sub description)")
|
537
|
+
end
|
538
|
+
|
539
|
+
it 'can access helper methods from `failure_message`' do
|
540
|
+
matcher = new_matcher(:positive_failure_message) do
|
541
|
+
def helper() "helper" end
|
542
|
+
failure_message { helper }
|
543
|
+
end
|
544
|
+
|
545
|
+
expect(matcher.failure_message).to eq("helper")
|
546
|
+
end
|
547
|
+
|
548
|
+
it 'can access helper methods from `failure_message_when_negated`' do
|
549
|
+
matcher = new_matcher(:negative_failure_message) do
|
550
|
+
def helper() "helper" end
|
551
|
+
failure_message_when_negated { helper }
|
552
|
+
end
|
553
|
+
|
554
|
+
expect(matcher.failure_message_when_negated).to eq("helper")
|
555
|
+
end
|
556
|
+
|
557
|
+
it 'can exit early with a `return` from `description` just like in a method' do
|
558
|
+
matcher = new_matcher(:desc) do
|
559
|
+
description { return "Desc" }
|
560
|
+
end
|
561
|
+
|
562
|
+
expect(matcher.description).to eq("Desc")
|
563
|
+
end
|
564
|
+
|
565
|
+
it 'can exit early with a `return` from `failure_message` just like in a method' do
|
566
|
+
matcher = new_matcher(:positive_failure_message) do
|
567
|
+
failure_message { return "msg" }
|
568
|
+
end
|
569
|
+
|
570
|
+
expect(matcher.failure_message).to eq("msg")
|
571
|
+
end
|
572
|
+
|
573
|
+
it 'can exit early with a `return` from `failure_message_when_negated` just like in a method' do
|
574
|
+
matcher = new_matcher(:negative_failure_message) do
|
575
|
+
failure_message_when_negated { return "msg" }
|
576
|
+
end
|
577
|
+
|
578
|
+
expect(matcher.failure_message_when_negated).to eq("msg")
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
context "#new" do
|
583
|
+
it "passes matches? arg to match block" do
|
584
|
+
matcher = new_matcher(:ignore) do
|
585
|
+
match do |actual|
|
586
|
+
actual == 5
|
587
|
+
end
|
588
|
+
end
|
589
|
+
expect(matcher.matches?(5)).to be_truthy
|
590
|
+
end
|
591
|
+
|
592
|
+
it "exposes arg submitted through #new to matcher block" do
|
593
|
+
matcher = new_matcher(:ignore, 4) do |expected|
|
594
|
+
match do |actual|
|
595
|
+
actual > expected
|
596
|
+
end
|
597
|
+
end
|
598
|
+
expect(matcher.matches?(5)).to be_truthy
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
context "with no args" do
|
603
|
+
let(:matcher) do
|
604
|
+
new_matcher(:matcher_name) do
|
605
|
+
match do |actual|
|
606
|
+
actual == 5
|
607
|
+
end
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
it "matches" do
|
612
|
+
expect(matcher.matches?(5)).to be_truthy
|
613
|
+
end
|
614
|
+
|
615
|
+
it "describes" do
|
616
|
+
expect(matcher.description).to eq "matcher name"
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
context "with 1 arg" do
|
621
|
+
let(:matcher) do
|
622
|
+
new_matcher(:matcher_name, 1) do |expected|
|
623
|
+
match do |actual|
|
624
|
+
actual == 5 && expected == 1
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
it "matches" do
|
630
|
+
expect(matcher.matches?(5)).to be_truthy
|
631
|
+
end
|
632
|
+
|
633
|
+
it "describes" do
|
634
|
+
expect(matcher.description).to eq "matcher name 1"
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
context "with multiple args" do
|
639
|
+
let(:matcher) do
|
640
|
+
new_matcher(:matcher_name, 1, 2, 3, 4) do |a, b, c, d|
|
641
|
+
match do |sum|
|
642
|
+
a + b + c + d == sum
|
643
|
+
end
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
it "matches" do
|
648
|
+
expect(matcher.matches?(10)).to be_truthy
|
649
|
+
end
|
650
|
+
|
651
|
+
it "describes" do
|
652
|
+
expect(matcher.description).to eq "matcher name 1, 2, 3, and 4"
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
656
|
+
it "supports helper methods" do
|
657
|
+
matcher = new_matcher(:be_similar_to, [1, 2, 3]) do |sample|
|
658
|
+
match do |actual|
|
659
|
+
similar?(sample, actual)
|
660
|
+
end
|
661
|
+
|
662
|
+
def similar?(a, b)
|
663
|
+
a.sort == b.sort
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
expect(matcher.matches?([2,3,1])).to be_truthy
|
668
|
+
end
|
669
|
+
|
670
|
+
it "supports fluent interface" do
|
671
|
+
matcher = new_matcher(:first_word) do
|
672
|
+
def second_word
|
673
|
+
self
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
expect(matcher.second_word).to eq matcher
|
678
|
+
end
|
679
|
+
|
680
|
+
it "treats method missing normally for undeclared methods" do
|
681
|
+
matcher = new_matcher(:ignore) { }
|
682
|
+
expect { matcher.non_existent_method }.to raise_error(NoMethodError)
|
683
|
+
end
|
684
|
+
|
685
|
+
it "has access to other matchers" do
|
686
|
+
matcher = new_matcher(:ignore, 3) do |expected|
|
687
|
+
match do |actual|
|
688
|
+
extend RSpec::Matchers
|
689
|
+
expect(actual).to eql(5 + expected)
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
expect(matcher.matches?(8)).to be_truthy
|
694
|
+
end
|
695
|
+
|
696
|
+
context 'when multiple instances of the same matcher are used in the same example' do
|
697
|
+
RSpec::Matchers.define(:be_like_a) do |expected|
|
698
|
+
match { |actual| actual == expected }
|
699
|
+
description { "be like a #{expected}" }
|
700
|
+
failure_message { "expected to be like a #{expected}" }
|
701
|
+
failure_message_when_negated { "expected not to be like a #{expected}" }
|
702
|
+
end
|
703
|
+
|
704
|
+
# Note: these bugs were only exposed when creating both instances
|
705
|
+
# first, then checking their descriptions/failure messages.
|
706
|
+
#
|
707
|
+
# That's why we eager-instantiate them here.
|
708
|
+
let!(:moose) { be_like_a("moose") }
|
709
|
+
let!(:horse) { be_like_a("horse") }
|
710
|
+
|
711
|
+
it 'allows them to use the expected value in the description' do
|
712
|
+
expect(horse.description).to eq("be like a horse")
|
713
|
+
expect(moose.description).to eq("be like a moose")
|
714
|
+
end
|
715
|
+
|
716
|
+
it 'allows them to use the expected value in the positive failure message' do
|
717
|
+
expect(moose.failure_message).to eq("expected to be like a moose")
|
718
|
+
expect(horse.failure_message).to eq("expected to be like a horse")
|
719
|
+
end
|
720
|
+
|
721
|
+
it 'allows them to use the expected value in the negative failure message' do
|
722
|
+
expect(moose.failure_message_when_negated).to eq("expected not to be like a moose")
|
723
|
+
expect(horse.failure_message_when_negated).to eq("expected not to be like a horse")
|
724
|
+
end
|
725
|
+
|
726
|
+
it 'allows them to match separately' do
|
727
|
+
expect("moose").to moose
|
728
|
+
expect("horse").to horse
|
729
|
+
expect("horse").not_to moose
|
730
|
+
expect("moose").not_to horse
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
describe "#match_unless_raises" do
|
735
|
+
context "with an assertion" do
|
736
|
+
mod = Module.new do
|
737
|
+
def assert_equal(a,b)
|
738
|
+
raise UnexpectedError.new("#{b} does not equal #{a}") unless a == b
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
let(:matcher) do
|
743
|
+
new_matcher(:equal, 4) do |expected|
|
744
|
+
include mod
|
745
|
+
match_unless_raises UnexpectedError do
|
746
|
+
assert_equal expected, actual
|
747
|
+
end
|
748
|
+
end
|
749
|
+
end
|
750
|
+
|
751
|
+
context "with passing assertion" do
|
752
|
+
it "passes" do
|
753
|
+
expect(matcher.matches?(4)).to be_truthy
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
757
|
+
context "with failing assertion" do
|
758
|
+
it "fails" do
|
759
|
+
expect(matcher.matches?(5)).to be_falsey
|
760
|
+
end
|
761
|
+
|
762
|
+
it "provides the raised exception" do
|
763
|
+
matcher.matches?(5)
|
764
|
+
expect(matcher.rescued_exception.message).to eq("5 does not equal 4")
|
765
|
+
end
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
769
|
+
context "with an unexpected error" do
|
770
|
+
let(:matcher) do
|
771
|
+
new_matcher(:foo, :bar) do |expected|
|
772
|
+
match_unless_raises SyntaxError do |actual|
|
773
|
+
raise "unexpected exception"
|
774
|
+
end
|
775
|
+
end
|
776
|
+
end
|
777
|
+
|
778
|
+
it "raises the error" do
|
779
|
+
expect {
|
780
|
+
matcher.matches?(:bar)
|
781
|
+
}.to raise_error("unexpected exception")
|
782
|
+
end
|
783
|
+
end
|
784
|
+
|
785
|
+
context "without a specified error class" do
|
786
|
+
let(:matcher) do
|
787
|
+
new_matcher(:foo) do
|
788
|
+
match_unless_raises do |actual|
|
789
|
+
raise Exception unless actual == 5
|
790
|
+
end
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
it 'passes if no error is raised' do
|
795
|
+
expect(matcher.matches?(5)).to be true
|
796
|
+
end
|
797
|
+
|
798
|
+
it 'fails if an exception is raised' do
|
799
|
+
expect(matcher.matches?(4)).to be false
|
800
|
+
end
|
801
|
+
end
|
802
|
+
|
803
|
+
end
|
804
|
+
|
805
|
+
it "can define chainable methods" do
|
806
|
+
matcher = new_matcher(:name) do
|
807
|
+
chain(:expecting) do |expected_value|
|
808
|
+
@expected_value = expected_value
|
809
|
+
end
|
810
|
+
match { |actual| actual == @expected_value }
|
811
|
+
end
|
812
|
+
|
813
|
+
expect(matcher.expecting('value').matches?('value')).to be_truthy
|
814
|
+
expect(matcher.expecting('value').matches?('other value')).to be_falsey
|
815
|
+
end
|
816
|
+
|
817
|
+
it 'can use an early return from a `chain` block' do
|
818
|
+
matcher = new_matcher(:name) do
|
819
|
+
chain(:expecting) do |expected_value|
|
820
|
+
@expected_value = expected_value
|
821
|
+
return
|
822
|
+
end
|
823
|
+
match { |actual| actual == @expected_value }
|
824
|
+
end
|
825
|
+
|
826
|
+
expect(matcher.expecting('value').matches?('value')).to be_truthy
|
827
|
+
expect(matcher.expecting('value').matches?('other value')).to be_falsey
|
828
|
+
end
|
829
|
+
|
830
|
+
it 'allows chainable methods to accept blocks' do
|
831
|
+
matcher = new_matcher(:name) do
|
832
|
+
chain(:for_block) { |&b| @block = b }
|
833
|
+
match { |value| @block.call == value }
|
834
|
+
end
|
835
|
+
|
836
|
+
expect(matcher.for_block { 5 }.matches?(5)).to be true
|
837
|
+
expect(matcher.for_block { 3 }.matches?(4)).to be false
|
838
|
+
end
|
839
|
+
|
840
|
+
it "prevents name collisions on chainable methods from different matchers" do
|
841
|
+
m1 = new_matcher(:m1) { chain(:foo) { raise "foo in m1" } }
|
842
|
+
m2 = new_matcher(:m2) { chain(:foo) { raise "foo in m2" } }
|
843
|
+
|
844
|
+
expect { m1.foo }.to raise_error("foo in m1")
|
845
|
+
expect { m2.foo }.to raise_error("foo in m2")
|
846
|
+
end
|
847
|
+
|
848
|
+
context "defined using the dsl" do
|
849
|
+
def a_method_in_the_example
|
850
|
+
"method defined in the example"
|
851
|
+
end
|
852
|
+
|
853
|
+
it "can access methods in the running example" do |example|
|
854
|
+
RSpec::Matchers.define(:__access_running_example) do
|
855
|
+
match do |actual|
|
856
|
+
a_method_in_the_example == "method defined in the example"
|
857
|
+
end
|
858
|
+
end
|
859
|
+
expect(example).to __access_running_example
|
860
|
+
end
|
861
|
+
|
862
|
+
it 'can get a method object for methods in the running example', :if => (RUBY_VERSION.to_f > 1.8) do
|
863
|
+
matcher = new_matcher(:get_method_object) { }
|
864
|
+
method = matcher.method(:a_method_in_the_example)
|
865
|
+
expect(method.call).to eq("method defined in the example")
|
866
|
+
end
|
867
|
+
|
868
|
+
it 'indicates that it responds to a method from the running example' do
|
869
|
+
matcher = new_matcher(:respond_to) { }
|
870
|
+
expect(matcher).to respond_to(:a_method_in_the_example)
|
871
|
+
expect(matcher).not_to respond_to(:a_method_not_in_the_example)
|
872
|
+
end
|
873
|
+
|
874
|
+
it "raises NoMethodError for methods not in the running_example" do |example|
|
875
|
+
RSpec::Matchers.define(:__raise_no_method_error) do
|
876
|
+
match do |actual|
|
877
|
+
self.a_method_not_in_the_example == "method defined in the example"
|
878
|
+
end
|
879
|
+
end
|
880
|
+
|
881
|
+
expected_msg = "RSpec::Matchers::DSL::Matcher"
|
882
|
+
expected_msg << " __raise_no_method_error" unless rbx?
|
883
|
+
|
884
|
+
expect {
|
885
|
+
expect(example).to __raise_no_method_error
|
886
|
+
}.to raise_error(NoMethodError, /#{expected_msg}/)
|
887
|
+
end
|
888
|
+
|
889
|
+
def rbx?
|
890
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
894
|
+
end
|
895
|
+
end
|