rspec-expectations 3.0.0.beta1 → 3.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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