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.
Files changed (122) hide show
  1. data.tar.gz.sig +2 -2
  2. data/.yardopts +1 -0
  3. data/Changelog.md +138 -0
  4. data/README.md +75 -8
  5. data/features/README.md +2 -2
  6. data/features/built_in_matchers/README.md +12 -9
  7. data/features/built_in_matchers/comparisons.feature +2 -2
  8. data/features/built_in_matchers/contain_exactly.feature +46 -0
  9. data/features/built_in_matchers/expect_change.feature +2 -2
  10. data/features/built_in_matchers/include.feature +0 -48
  11. data/features/built_in_matchers/output.feature +70 -0
  12. data/features/composing_matchers.feature +250 -0
  13. data/features/compound_expectations.feature +45 -0
  14. data/features/custom_matchers/access_running_example.feature +1 -1
  15. data/features/custom_matchers/define_matcher.feature +6 -6
  16. data/features/custom_matchers/define_matcher_outside_rspec.feature +4 -8
  17. data/features/test_frameworks/{test_unit.feature → minitest.feature} +11 -11
  18. data/lib/rspec/expectations.rb +31 -42
  19. data/lib/rspec/expectations/diff_presenter.rb +141 -0
  20. data/lib/rspec/expectations/differ.rb +22 -132
  21. data/lib/rspec/expectations/encoded_string.rb +56 -0
  22. data/lib/rspec/expectations/expectation_target.rb +0 -30
  23. data/lib/rspec/expectations/fail_with.rb +2 -2
  24. data/lib/rspec/expectations/handler.rb +128 -31
  25. data/lib/rspec/expectations/minitest_integration.rb +16 -0
  26. data/lib/rspec/expectations/syntax.rb +4 -58
  27. data/lib/rspec/expectations/version.rb +1 -1
  28. data/lib/rspec/matchers.rb +298 -60
  29. data/lib/rspec/matchers/aliased_matcher.rb +35 -0
  30. data/lib/rspec/matchers/built_in.rb +37 -33
  31. data/lib/rspec/matchers/built_in/base_matcher.rb +25 -15
  32. data/lib/rspec/matchers/built_in/be.rb +23 -31
  33. data/lib/rspec/matchers/built_in/be_between.rb +55 -0
  34. data/lib/rspec/matchers/built_in/be_within.rb +15 -11
  35. data/lib/rspec/matchers/built_in/change.rb +198 -81
  36. data/lib/rspec/matchers/built_in/compound.rb +106 -0
  37. data/lib/rspec/matchers/built_in/contain_exactly.rb +245 -0
  38. data/lib/rspec/matchers/built_in/eq.rb +43 -4
  39. data/lib/rspec/matchers/built_in/eql.rb +2 -2
  40. data/lib/rspec/matchers/built_in/equal.rb +35 -18
  41. data/lib/rspec/matchers/built_in/has.rb +16 -15
  42. data/lib/rspec/matchers/built_in/include.rb +45 -23
  43. data/lib/rspec/matchers/built_in/match.rb +6 -3
  44. data/lib/rspec/matchers/built_in/operators.rb +103 -0
  45. data/lib/rspec/matchers/built_in/output.rb +108 -0
  46. data/lib/rspec/matchers/built_in/raise_error.rb +9 -15
  47. data/lib/rspec/matchers/built_in/respond_to.rb +5 -4
  48. data/lib/rspec/matchers/built_in/satisfy.rb +4 -3
  49. data/lib/rspec/matchers/built_in/start_and_end_with.rb +37 -16
  50. data/lib/rspec/matchers/built_in/throw_symbol.rb +6 -5
  51. data/lib/rspec/matchers/built_in/yield.rb +31 -29
  52. data/lib/rspec/matchers/composable.rb +138 -0
  53. data/lib/rspec/matchers/dsl.rb +330 -0
  54. data/lib/rspec/matchers/generated_descriptions.rb +6 -6
  55. data/lib/rspec/matchers/matcher_delegator.rb +33 -0
  56. data/lib/rspec/matchers/pretty.rb +13 -2
  57. data/spec/rspec/expectations/{differ_spec.rb → diff_presenter_spec.rb} +56 -36
  58. data/spec/rspec/expectations/encoded_string_spec.rb +74 -0
  59. data/spec/rspec/expectations/extensions/kernel_spec.rb +11 -11
  60. data/spec/rspec/expectations/fail_with_spec.rb +8 -8
  61. data/spec/rspec/expectations/handler_spec.rb +27 -49
  62. data/spec/rspec/expectations/minitest_integration_spec.rb +27 -0
  63. data/spec/rspec/expectations/syntax_spec.rb +17 -67
  64. data/spec/rspec/expectations_spec.rb +7 -52
  65. data/spec/rspec/matchers/aliased_matcher_spec.rb +48 -0
  66. data/spec/rspec/matchers/aliases_spec.rb +449 -0
  67. data/spec/rspec/matchers/{base_matcher_spec.rb → built_in/base_matcher_spec.rb} +24 -3
  68. data/spec/rspec/matchers/built_in/be_between_spec.rb +159 -0
  69. data/spec/rspec/matchers/{be_instance_of_spec.rb → built_in/be_instance_of_spec.rb} +0 -0
  70. data/spec/rspec/matchers/{be_kind_of_spec.rb → built_in/be_kind_of_spec.rb} +0 -0
  71. data/spec/rspec/matchers/{be_spec.rb → built_in/be_spec.rb} +76 -32
  72. data/spec/rspec/matchers/{be_within_spec.rb → built_in/be_within_spec.rb} +6 -2
  73. data/spec/rspec/matchers/{change_spec.rb → built_in/change_spec.rb} +310 -69
  74. data/spec/rspec/matchers/built_in/compound_spec.rb +292 -0
  75. data/spec/rspec/matchers/built_in/contain_exactly_spec.rb +441 -0
  76. data/spec/rspec/matchers/{cover_spec.rb → built_in/cover_spec.rb} +0 -0
  77. data/spec/rspec/matchers/built_in/eq_spec.rb +156 -0
  78. data/spec/rspec/matchers/{eql_spec.rb → built_in/eql_spec.rb} +2 -2
  79. data/spec/rspec/matchers/built_in/equal_spec.rb +106 -0
  80. data/spec/rspec/matchers/{exist_spec.rb → built_in/exist_spec.rb} +1 -1
  81. data/spec/rspec/matchers/{has_spec.rb → built_in/has_spec.rb} +39 -0
  82. data/spec/rspec/matchers/{include_spec.rb → built_in/include_spec.rb} +118 -109
  83. data/spec/rspec/matchers/{match_spec.rb → built_in/match_spec.rb} +30 -2
  84. data/spec/rspec/matchers/{operator_matcher_spec.rb → built_in/operators_spec.rb} +26 -26
  85. data/spec/rspec/matchers/built_in/output_spec.rb +165 -0
  86. data/spec/rspec/matchers/{raise_error_spec.rb → built_in/raise_error_spec.rb} +81 -11
  87. data/spec/rspec/matchers/{respond_to_spec.rb → built_in/respond_to_spec.rb} +0 -0
  88. data/spec/rspec/matchers/{satisfy_spec.rb → built_in/satisfy_spec.rb} +0 -0
  89. data/spec/rspec/matchers/{start_with_end_with_spec.rb → built_in/start_and_end_with_spec.rb} +82 -15
  90. data/spec/rspec/matchers/{throw_symbol_spec.rb → built_in/throw_symbol_spec.rb} +29 -10
  91. data/spec/rspec/matchers/{yield_spec.rb → built_in/yield_spec.rb} +90 -0
  92. data/spec/rspec/matchers/configuration_spec.rb +7 -39
  93. data/spec/rspec/matchers/description_generation_spec.rb +22 -6
  94. data/spec/rspec/matchers/dsl_spec.rb +838 -0
  95. data/spec/rspec/matchers/legacy_spec.rb +101 -0
  96. data/spec/rspec/matchers_spec.rb +74 -0
  97. data/spec/spec_helper.rb +35 -21
  98. data/spec/support/shared_examples.rb +26 -4
  99. metadata +172 -116
  100. metadata.gz.sig +3 -4
  101. checksums.yaml +0 -15
  102. checksums.yaml.gz.sig +0 -0
  103. data/features/built_in_matchers/match_array.feature +0 -37
  104. data/lib/rspec/expectations/errors.rb +0 -9
  105. data/lib/rspec/expectations/extensions.rb +0 -1
  106. data/lib/rspec/expectations/extensions/object.rb +0 -29
  107. data/lib/rspec/matchers/built_in/match_array.rb +0 -51
  108. data/lib/rspec/matchers/compatibility.rb +0 -14
  109. data/lib/rspec/matchers/matcher.rb +0 -301
  110. data/lib/rspec/matchers/method_missing.rb +0 -12
  111. data/lib/rspec/matchers/operator_matcher.rb +0 -99
  112. data/lib/rspec/matchers/test_unit_integration.rb +0 -11
  113. data/spec/rspec/matchers/eq_spec.rb +0 -60
  114. data/spec/rspec/matchers/equal_spec.rb +0 -78
  115. data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
  116. data/spec/rspec/matchers/match_array_spec.rb +0 -194
  117. data/spec/rspec/matchers/matcher_spec.rb +0 -706
  118. data/spec/rspec/matchers/matchers_spec.rb +0 -36
  119. data/spec/rspec/matchers/method_missing_spec.rb +0 -28
  120. data/spec/support/classes.rb +0 -56
  121. data/spec/support/in_sub_process.rb +0 -37
  122. 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