rspec-expectations 2.14.5 → 2.99.0.beta1

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.
@@ -11,7 +11,11 @@ module RSpec
11
11
  else expected
12
12
  end
13
13
  @relativity = relativity
14
+
14
15
  @actual = @collection_name = @plural_collection_name = nil
16
+ @target_owns_a_collection = false
17
+ @negative_expectation = false
18
+ @expectation_format_method = "to"
15
19
  end
16
20
 
17
21
  def relativities
@@ -29,13 +33,20 @@ module RSpec
29
33
  for query_method in QUERY_METHODS
30
34
  next unless collection.respond_to?(query_method)
31
35
  @actual = collection.__send__(query_method)
32
- break unless @actual.nil?
36
+
37
+ if @actual
38
+ print_deprecation_message(query_method)
39
+ break
40
+ end
33
41
  end
42
+
34
43
  raise not_a_collection if @actual.nil?
35
44
  else
36
45
  query_method = determine_query_method(collection)
37
46
  raise not_a_collection unless query_method
38
47
  @actual = collection.__send__(query_method)
48
+
49
+ print_deprecation_message(query_method)
39
50
  end
40
51
  case @relativity
41
52
  when :at_least then @actual >= @expected
@@ -45,10 +56,18 @@ module RSpec
45
56
  end
46
57
  alias == matches?
47
58
 
59
+ def does_not_match?(collection_or_owner)
60
+ @negative_expectation = true
61
+ @expectation_format_method = "to_not"
62
+ !matches?(collection_or_owner)
63
+ end
64
+
48
65
  def determine_collection(collection_or_owner)
49
66
  if collection_or_owner.respond_to?(@collection_name)
67
+ @target_owns_a_collection = true
50
68
  collection_or_owner.__send__(@collection_name, *@args, &@block)
51
69
  elsif (@plural_collection_name && collection_or_owner.respond_to?(@plural_collection_name))
70
+ @target_owns_a_collection = true
52
71
  collection_or_owner.__send__(@plural_collection_name, *@args, &@block)
53
72
  elsif determine_query_method(collection_or_owner)
54
73
  collection_or_owner
@@ -118,6 +137,83 @@ EOF
118
137
  def enumerator_class
119
138
  RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator
120
139
  end
140
+
141
+ def print_deprecation_message(query_method)
142
+ deprecation_message = "the rspec-collection_matchers gem "
143
+ deprecation_message << "or replace your expectation with something like "
144
+ deprecation_message << "`expect(#{cardinality_expression(query_method)}).#{expectation_format_method} #{suggested_matcher_expression}`"
145
+
146
+ RSpec.deprecate("`#{expectation_expression(query_method)}`", :replacement => deprecation_message)
147
+ end
148
+
149
+ def expectation_expression(query_method)
150
+ if @negative_expectation
151
+ RSpec::Expectations::Syntax.negative_expression(target_expression, original_matcher_expression)
152
+ else
153
+ RSpec::Expectations::Syntax.positive_expression(target_expression, original_matcher_expression)
154
+ end
155
+ end
156
+
157
+ def target_expression
158
+ if @target_owns_a_collection
159
+ 'collection_owner'
160
+ else
161
+ 'collection'
162
+ end
163
+ end
164
+
165
+ def original_matcher_expression
166
+ "#{matcher_method}(#{@expected}).#{@collection_name}"
167
+ end
168
+
169
+ def expectation_format_method
170
+ if @relativity == :exactly
171
+ @expectation_format_method
172
+ else
173
+ "to"
174
+ end
175
+ end
176
+
177
+ def cardinality_expression(query_method)
178
+ expression = "#{target_expression}."
179
+ expression << "#{@collection_name}." if @target_owns_a_collection
180
+ expression << String(query_method)
181
+ end
182
+
183
+ def suggested_matcher_expression
184
+ send("suggested_matcher_expression_for_#{@relativity}")
185
+ end
186
+
187
+ def suggested_matcher_expression_for_exactly
188
+ "eq(#{@expected})"
189
+ end
190
+
191
+ def suggested_matcher_expression_for_at_most
192
+ if @negative_expectation
193
+ "be > #{@expected}"
194
+ else
195
+ "be <= #{@expected}"
196
+ end
197
+ end
198
+
199
+ def suggested_matcher_expression_for_at_least
200
+ if @negative_expectation
201
+ "be < #{@expected}"
202
+ else
203
+ "be >= #{@expected}"
204
+ end
205
+ end
206
+
207
+ def matcher_method
208
+ case @relativity
209
+ when :exactly
210
+ "have"
211
+ when :at_most
212
+ "have_at_most"
213
+ when :at_least
214
+ "have_at_least"
215
+ end
216
+ end
121
217
  end
122
218
  end
123
219
  end
@@ -0,0 +1,55 @@
1
+ module RSpec
2
+ module Matchers
3
+ # Evaluates a block in order to determine what methods, if any,
4
+ # it defines as instance methods (using `def foo`) vs singleton
5
+ # methods (using `def self.foo`).
6
+ #
7
+ # @api private
8
+ class DifferentiateBlockMethodTypes
9
+ def initialize(*block_args, &block)
10
+ @block_args = block_args
11
+ @block = block
12
+
13
+ ignore_macro_methods
14
+
15
+ capture_added_methods(singletons_singleton_class, singleton_methods)
16
+ capture_added_methods(singleton_class, instance_methods)
17
+
18
+ singleton_class.class_exec(*block_args, &block)
19
+ end
20
+
21
+ def singleton_methods
22
+ @singleton_methods ||= []
23
+ end
24
+
25
+ def instance_methods
26
+ @instance_methods ||= []
27
+ end
28
+
29
+ private
30
+
31
+ def capture_added_methods(object, method_list)
32
+ object.__send__(:define_method, :singleton_method_added) do |method_name|
33
+ method_list << method_name
34
+ end
35
+
36
+ method_list.delete(:singleton_method_added)
37
+ end
38
+
39
+ unless method_defined?(:singleton_class)
40
+ def singleton_class
41
+ class << self; self; end
42
+ end
43
+ end
44
+
45
+ def singletons_singleton_class
46
+ class << singleton_class; self; end
47
+ end
48
+
49
+ def ignore_macro_methods
50
+ def singleton_class.method_missing(*); self; end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -1,4 +1,5 @@
1
1
  require 'set'
2
+ require 'rspec/matchers/differentiate_block_method_types'
2
3
 
3
4
  module RSpec
4
5
  module Matchers
@@ -22,6 +23,9 @@ module RSpec
22
23
  @expected_exception, @rescued_exception = nil, nil
23
24
  @match_for_should_not_block = nil
24
25
  @messages = {}
26
+ @define_block_executed = false
27
+ @block_method_differentiator = nil
28
+ @deprecated_methods = Set.new
25
29
  end
26
30
 
27
31
  PERSISTENT_INSTANCE_VARIABLES = [
@@ -38,9 +42,14 @@ module RSpec
38
42
  instance_variable_set(ivar, nil) unless (PERSISTENT_INSTANCE_VARIABLES + [:@expected]).include?(ivar)
39
43
  end
40
44
  @messages = {}
45
+ @deprecated_methods = Set.new
46
+
47
+ @block_method_differentiator = DifferentiateBlockMethodTypes.new(*@expected, &@declarations)
41
48
  making_declared_methods_public do
42
49
  instance_eval_with_args(*@expected, &@declarations)
43
50
  end
51
+
52
+ @define_block_executed = true
44
53
  self
45
54
  end
46
55
  end
@@ -237,8 +246,38 @@ module RSpec
237
246
  end
238
247
  end
239
248
 
240
- def include(*args)
241
- singleton_class.__send__(:include, *args)
249
+ def include(*modules)
250
+ return_value = singleton_class.__send__(:include, *modules)
251
+
252
+ modules.each do |mod|
253
+ mod.instance_methods.each do |name|
254
+ add_deprecation_warning_to(name,
255
+ "Calling a helper method (`#{name}`) from a module included in a custom matcher as a macro",
256
+ "`extend #{mod.name || "TheModule"}`",
257
+ "included in the custom matcher",
258
+ :unless
259
+ )
260
+ end
261
+ end
262
+
263
+ return_value
264
+ end
265
+
266
+ def extend(*modules)
267
+ return_value = super
268
+
269
+ modules.each do |mod|
270
+ mod.instance_methods.each do |name|
271
+ add_deprecation_warning_to(name,
272
+ "Calling a helper method (`#{name}`) from a module extended onto a custom matcher",
273
+ "`include #{mod.name || "TheModule"}`",
274
+ "extended onto the custom matcher",
275
+ :if
276
+ )
277
+ end
278
+ end unless @define_block_executed
279
+
280
+ return_value
242
281
  end
243
282
 
244
283
  def define_method(name, &block)
@@ -294,6 +333,50 @@ module RSpec
294
333
  class << self; self; end
295
334
  end
296
335
  end
336
+
337
+ def singleton_method_added(name)
338
+ return unless @block_method_differentiator
339
+
340
+ if @block_method_differentiator.instance_methods.include?(name)
341
+ add_deprecation_warning_to(name,
342
+ "Calling a helper method (`#{name}`) defined as an instance method (using `def #{name}`) as a macro from a custom matcher `define` block",
343
+ "`def self.#{name}` (to define it as a singleton method)",
344
+ "defined in the custom matcher definition block",
345
+ :unless
346
+ )
347
+ elsif @block_method_differentiator.singleton_methods.include?(name)
348
+ add_deprecation_warning_to(name,
349
+ "Calling a helper method (`#{name}`) defined as a singleton method (using `def self.#{name}`) on a custom matcher",
350
+ "`def #{name}` (to define it as an instance method)",
351
+ "defined in the custom matcher definition block",
352
+ :if
353
+ )
354
+ end
355
+ end
356
+
357
+ def add_deprecation_warning_to(method_name, msg, replacement, extra_call_site_msg, condition)
358
+ return if @deprecated_methods.include?(method_name)
359
+ @deprecated_methods << method_name
360
+
361
+ aliased_name = aliased_name_for(method_name)
362
+ singleton_class.__send__(:alias_method, aliased_name, method_name)
363
+
364
+ singleton_class.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
365
+ def #{method_name}(*a, &b)
366
+ ::RSpec.deprecate(#{msg.inspect},
367
+ :replacement => #{replacement.inspect},
368
+ :call_site => CallerFilter.first_non_rspec_line + " and #{extra_call_site_msg} at #{CallerFilter.first_non_rspec_line}"
369
+ ) #{condition} @define_block_executed
370
+
371
+ __send__(#{aliased_name.inspect}, *a, &b)
372
+ end
373
+ EOS
374
+ end
375
+
376
+ def aliased_name_for(method_name)
377
+ target, punctuation = method_name.to_s.sub(/([?!=])$/, ''), $1
378
+ "#{target}_without_rspec_deprecation_warning#{punctuation}"
379
+ end
297
380
  end
298
381
  end
299
382
  end
@@ -140,7 +140,7 @@ module RSpec
140
140
  actual = Object.new
141
141
  matcher.should_receive(:matches?).with(actual).and_return(false)
142
142
  matcher.stub(:negative_failure_message).and_return("ignore")
143
- expect(RSpec::Expectations::NegativeExpectationHandler.handle_matcher(actual, matcher)).to be_false
143
+ expect(RSpec::Expectations::NegativeExpectationHandler.handle_matcher(actual, matcher)).to be_falsey
144
144
  end
145
145
 
146
146
 
@@ -91,8 +91,8 @@ module RSpec
91
91
 
92
92
  context "when only :expect is enabled" do
93
93
  before do
94
- expect(Syntax.should_enabled?).to be_false
95
- expect(Syntax.expect_enabled?).to be_true
94
+ expect(Syntax.should_enabled?).to be_falsey
95
+ expect(Syntax.expect_enabled?).to be_truthy
96
96
  end
97
97
 
98
98
  it 'generates a positive expression using the expect syntax' do
@@ -106,8 +106,8 @@ module RSpec
106
106
 
107
107
  context "when both :should and :expect are enabled", :uses_should do
108
108
  before do
109
- expect(Syntax.should_enabled?).to be_true
110
- expect(Syntax.expect_enabled?).to be_true
109
+ expect(Syntax.should_enabled?).to be_truthy
110
+ expect(Syntax.expect_enabled?).to be_truthy
111
111
  end
112
112
 
113
113
  it 'generates a positive expression using the expect syntax' do
@@ -121,8 +121,8 @@ module RSpec
121
121
 
122
122
  context "when only :should is enabled", :uses_only_should do
123
123
  before do
124
- Syntax.should_enabled?.should be_true
125
- Syntax.expect_enabled?.should be_false
124
+ Syntax.should_enabled?.should be_truthy
125
+ Syntax.expect_enabled?.should be_falsey
126
126
  end
127
127
 
128
128
  it 'generates a positive expression using the expect syntax' do
@@ -1,5 +1,19 @@
1
1
  module RSpec
2
2
  describe Expectations do
3
+ def file_contents_for(lib, filename)
4
+ # http://rubular.com/r/HYpUMftlG2
5
+ path = $LOAD_PATH.find { |p| p.match(/\/rspec-#{lib}(-[a-f0-9]+)?\/lib/) }
6
+ file = File.join(path, filename)
7
+ File.read(file)
8
+ end
9
+
10
+ it 'has an up-to-date caller_filter file' do
11
+ expectations = file_contents_for("expectations", "rspec/expectations/caller_filter.rb")
12
+ core = file_contents_for("core", "rspec/core/caller_filter.rb")
13
+
14
+ expect(expectations).to eq(core)
15
+ end
16
+
3
17
  describe '.method_handle_for(object, method_name)' do
4
18
 
5
19
  class UntamperedClass
@@ -8,21 +8,21 @@ module RSpec::Matchers::BuiltIn
8
8
  end
9
9
 
10
10
  it "returns true if there are no errors" do
11
- expect(matcher.match_unless_raises {}).to be_true
11
+ expect(matcher.match_unless_raises {}).to be_truthy
12
12
  end
13
13
 
14
14
  it "returns false if there is an error" do
15
- expect(matcher.match_unless_raises { raise }).to be_false
15
+ expect(matcher.match_unless_raises { raise }).to be_falsey
16
16
  end
17
17
 
18
18
  it "returns false if the only submitted error is raised" do
19
- expect(matcher.match_unless_raises(RuntimeError){ raise "foo" }).to be_false
19
+ expect(matcher.match_unless_raises(RuntimeError){ raise "foo" }).to be_falsey
20
20
  end
21
21
 
22
22
  it "returns false if any of several errors submitted is raised" do
23
- expect(matcher.match_unless_raises(RuntimeError, ArgumentError, NameError) { raise "foo" }).to be_false
24
- expect(matcher.match_unless_raises(RuntimeError, ArgumentError, NameError) { raise ArgumentError.new('') }).to be_false
25
- expect(matcher.match_unless_raises(RuntimeError, ArgumentError, NameError) { raise NameError.new('') }).to be_false
23
+ expect(matcher.match_unless_raises(RuntimeError, ArgumentError, NameError) { raise "foo" }).to be_falsey
24
+ expect(matcher.match_unless_raises(RuntimeError, ArgumentError, NameError) { raise ArgumentError.new('') }).to be_falsey
25
+ expect(matcher.match_unless_raises(RuntimeError, ArgumentError, NameError) { raise NameError.new('') }).to be_falsey
26
26
  end
27
27
 
28
28
  it "re-raises any error other than one of those specified" do
@@ -39,27 +39,6 @@ module RSpec::Matchers::BuiltIn
39
39
 
40
40
  end
41
41
 
42
- describe "#failure_message_for_should" do
43
- context "when the parameter to .new is omitted" do
44
- it "describes what was expected" do
45
- matcher_class = Class.new(BaseMatcher) do
46
- def name=(name)
47
- @name = name
48
- end
49
-
50
- def match(expected, actual)
51
- false
52
- end
53
- end
54
-
55
- matcher = matcher_class.new
56
- matcher.name = "be something"
57
- matcher.matches?("foo")
58
- expect(matcher.failure_message_for_should).to eq('expected "foo" to be something')
59
- end
60
- end
61
- end
62
-
63
42
  describe "#==" do
64
43
  it "responds the same way as matches?" do
65
44
  matcher = Class.new(BaseMatcher) do
@@ -72,10 +51,10 @@ module RSpec::Matchers::BuiltIn
72
51
  end
73
52
  end
74
53
 
75
- expect(matcher.new(3).matches?(3)).to be_true
54
+ expect(matcher.new(3).matches?(3)).to be_truthy
76
55
  expect(matcher.new(3)).to eq(3)
77
56
 
78
- expect(matcher.new(3).matches?(4)).to be_false
57
+ expect(matcher.new(3).matches?(4)).to be_falsey
79
58
  expect(matcher.new(3)).not_to eq(4)
80
59
  end
81
60
  end
@@ -31,6 +31,21 @@ describe "expect(...).to be_predicate" do
31
31
  }.to raise_error(NameError, /happy\?/)
32
32
  end
33
33
 
34
+ it 'warns of deprecation when :predicate? is private' do
35
+ privately_happy = Class.new do
36
+ private
37
+ def happy?
38
+ true
39
+ end
40
+ end
41
+ expect(RSpec).to receive(:deprecate).with(
42
+ "matching with be_happy on private method happy?",
43
+ :replacement => "`expect(object.send(:happy?)).to be_true` or change the method's visibility to public",
44
+ :call_site => RSpec::Mocks::ArgumentMatchers::RegexpMatcher.new(/#{__FILE__}:#{__LINE__ + 2}/)
45
+ )
46
+ expect(privately_happy.new).to be_happy
47
+ end
48
+
34
49
  it "fails on error other than NameError" do
35
50
  actual = double("actual")
36
51
  actual.should_receive(:foo?).and_raise("aaaah")
@@ -50,7 +65,7 @@ describe "expect(...).to be_predicate" do
50
65
  it "does not support operator chaining like a basic `be` matcher does" do
51
66
  matcher = be_happy
52
67
  value = double(:happy? => false)
53
- expect(be_happy == value).to be false
68
+ expect(matcher == value).to be false
54
69
  end
55
70
  end
56
71
 
@@ -231,35 +246,94 @@ describe "expect(...).not_to be_predicate(*args, &block)" do
231
246
  end
232
247
  end
233
248
 
234
- describe "expect(...).to be_true" do
249
+ describe "expect(...).to be_truthy" do
235
250
  it "passes when actual equal?(true)" do
236
- expect(true).to be_true
251
+ expect(true).to be_truthy
237
252
  end
238
253
 
239
254
  it "passes when actual is 1" do
240
- expect(1).to be_true
255
+ expect(1).to be_truthy
241
256
  end
242
257
 
243
258
  it "fails when actual equal?(false)" do
244
259
  expect {
245
- expect(false).to be_true
246
- }.to fail_with("expected: true value\n got: false")
260
+ expect(false).to be_truthy
261
+ }.to fail_with("expected: truthy value\n got: false")
247
262
  end
248
263
  end
249
264
 
250
265
  describe "expect(...).to be_false" do
251
- it "passes when actual equal?(false)" do
266
+ it "is deprecated" do
267
+ expect(RSpec).to receive(:deprecate).with(/be_false/, :replacement => match(/falsey.*false/))
268
+ expect(false).to be_false
269
+ end
270
+
271
+ it "has the correct call site in the deprecation message" do
272
+ expect_deprecation_with_call_site(__FILE__, __LINE__ + 1)
252
273
  expect(false).to be_false
253
274
  end
254
275
 
276
+ it "still passes" do
277
+ allow(RSpec).to receive(:deprecate)
278
+ expect(false).to be_false
279
+ end
280
+
281
+ it "still fails" do
282
+ allow(RSpec).to receive(:deprecate)
283
+ expect {
284
+ expect(true).to be_false
285
+ }.to fail_with(/expected: falsey value/)
286
+ end
287
+ end
288
+
289
+ describe "expect(...).to be_true" do
290
+ it "is deprecated" do
291
+ expect(RSpec).to receive(:deprecate).with(/be_true/, :replacement => match(/truthy.*true/))
292
+ expect(true).to be_true
293
+ end
294
+
295
+ it "still passes" do
296
+ allow(RSpec).to receive(:deprecate)
297
+ expect(true).to be_true
298
+ end
299
+
300
+ it "still fails" do
301
+ allow(RSpec).to receive(:deprecate)
302
+ expect {
303
+ expect(false).to be_true
304
+ }.to fail_with(/expected: truthy value/)
305
+ end
306
+ end
307
+
308
+ describe "expect(...).to be_falsey" do
309
+ it "passes when actual equal?(false)" do
310
+ expect(false).to be_falsey
311
+ end
312
+
255
313
  it "passes when actual equal?(nil)" do
256
- expect(nil).to be_false
314
+ expect(nil).to be_falsey
257
315
  end
258
316
 
259
317
  it "fails when actual equal?(true)" do
260
318
  expect {
261
- expect(true).to be_false
262
- }.to fail_with("expected: false value\n got: true")
319
+ expect(true).to be_falsey
320
+ }.to fail_with("expected: falsey value\n got: true")
321
+ end
322
+ end
323
+
324
+ describe "expect(...).to be_falsy" do
325
+ it "passes when actual equal?(false)" do
326
+ expect(false).to be_falsy
327
+ end
328
+
329
+ it "passes when actual equal?(nil)" do
330
+ expect(nil).to be_falsy
331
+ end
332
+
333
+ it "fails when actual equal?(true)" do
334
+ expect {
335
+ expect(true).to be_falsy
336
+ }.to fail_with("expected: falsey value\n got: true")
263
337
  end
264
338
  end
265
339