rspec-expectations 2.12.1 → 2.13.0

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 (76) hide show
  1. data/Changelog.md +31 -0
  2. data/README.md +1 -1
  3. data/features/built_in_matchers/be.feature +6 -4
  4. data/features/built_in_matchers/be_within.feature +3 -1
  5. data/features/built_in_matchers/cover.feature +2 -0
  6. data/features/built_in_matchers/end_with.feature +2 -0
  7. data/features/built_in_matchers/equality.feature +9 -15
  8. data/features/built_in_matchers/exist.feature +2 -0
  9. data/features/built_in_matchers/expect_error.feature +14 -8
  10. data/features/built_in_matchers/have.feature +11 -5
  11. data/features/built_in_matchers/include.feature +53 -0
  12. data/features/built_in_matchers/match.feature +2 -0
  13. data/features/built_in_matchers/operators.feature +17 -11
  14. data/features/built_in_matchers/predicates.feature +21 -13
  15. data/features/built_in_matchers/respond_to.feature +7 -1
  16. data/features/built_in_matchers/satisfy.feature +2 -0
  17. data/features/built_in_matchers/start_with.feature +2 -0
  18. data/features/built_in_matchers/throw_symbol.feature +6 -0
  19. data/features/built_in_matchers/types.feature +8 -6
  20. data/lib/rspec/expectations/deprecation.rb +1 -1
  21. data/lib/rspec/expectations/differ.rb +8 -8
  22. data/lib/rspec/expectations/fail_with.rb +17 -3
  23. data/lib/rspec/expectations/syntax.rb +46 -0
  24. data/lib/rspec/expectations/version.rb +1 -1
  25. data/lib/rspec/matchers/built_in/be.rb +7 -3
  26. data/lib/rspec/matchers/built_in/be_within.rb +13 -4
  27. data/lib/rspec/matchers/built_in/change.rb +2 -2
  28. data/lib/rspec/matchers/built_in/equal.rb +5 -1
  29. data/lib/rspec/matchers/built_in/exist.rb +1 -1
  30. data/lib/rspec/matchers/built_in/have.rb +8 -8
  31. data/lib/rspec/matchers/built_in/include.rb +19 -3
  32. data/lib/rspec/matchers/built_in/respond_to.rb +1 -1
  33. data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +1 -1
  34. data/lib/rspec/matchers/matcher.rb +4 -3
  35. data/lib/rspec/matchers/operator_matcher.rb +1 -1
  36. data/lib/rspec/matchers/pretty.rb +5 -1
  37. data/spec/rspec/expectations/differ_spec.rb +8 -15
  38. data/spec/rspec/expectations/expectation_target_spec.rb +18 -8
  39. data/spec/rspec/expectations/extensions/kernel_spec.rb +15 -15
  40. data/spec/rspec/expectations/fail_with_spec.rb +41 -16
  41. data/spec/rspec/expectations/handler_spec.rb +13 -13
  42. data/spec/rspec/expectations/syntax_spec.rb +70 -8
  43. data/spec/rspec/matchers/base_matcher_spec.rb +14 -12
  44. data/spec/rspec/matchers/be_close_spec.rb +1 -1
  45. data/spec/rspec/matchers/be_instance_of_spec.rb +14 -8
  46. data/spec/rspec/matchers/be_kind_of_spec.rb +12 -8
  47. data/spec/rspec/matchers/be_spec.rb +212 -148
  48. data/spec/rspec/matchers/be_within_spec.rb +91 -42
  49. data/spec/rspec/matchers/change_spec.rb +52 -38
  50. data/spec/rspec/matchers/configuration_spec.rb +19 -15
  51. data/spec/rspec/matchers/cover_spec.rb +19 -19
  52. data/spec/rspec/matchers/description_generation_spec.rb +86 -86
  53. data/spec/rspec/matchers/dsl_spec.rb +7 -7
  54. data/spec/rspec/matchers/eq_spec.rb +17 -11
  55. data/spec/rspec/matchers/eql_spec.rb +10 -10
  56. data/spec/rspec/matchers/equal_spec.rb +27 -9
  57. data/spec/rspec/matchers/exist_spec.rb +35 -21
  58. data/spec/rspec/matchers/has_spec.rb +33 -29
  59. data/spec/rspec/matchers/have_spec.rb +165 -151
  60. data/spec/rspec/matchers/include_matcher_integration_spec.rb +30 -0
  61. data/spec/rspec/matchers/include_spec.rb +282 -124
  62. data/spec/rspec/matchers/match_array_spec.rb +90 -49
  63. data/spec/rspec/matchers/match_spec.rb +21 -21
  64. data/spec/rspec/matchers/matcher_spec.rb +85 -48
  65. data/spec/rspec/matchers/matchers_spec.rb +12 -6
  66. data/spec/rspec/matchers/method_missing_spec.rb +5 -1
  67. data/spec/rspec/matchers/operator_matcher_spec.rb +216 -237
  68. data/spec/rspec/matchers/raise_error_spec.rb +132 -132
  69. data/spec/rspec/matchers/respond_to_spec.rb +109 -112
  70. data/spec/rspec/matchers/satisfy_spec.rb +16 -16
  71. data/spec/rspec/matchers/start_with_end_with_spec.rb +36 -32
  72. data/spec/rspec/matchers/throw_symbol_spec.rb +24 -24
  73. data/spec/rspec/matchers/yield_spec.rb +7 -7
  74. data/spec/spec_helper.rb +46 -19
  75. data/spec/support/in_sub_process.rb +27 -20
  76. metadata +81 -83
@@ -3,8 +3,10 @@ Feature: satisfy matcher
3
3
  The satisfy matcher is extremely flexible and can handle almost anything
4
4
  you want to specify. It passes if the block you provide returns true:
5
5
 
6
+ ```ruby
6
7
  10.should satisfy { |v| v % 5 == 0 }
7
8
  7.should_not satisfy { |v| v % 5 == 0 }
9
+ ```
8
10
 
9
11
  This flexibility comes at a cost, however: the failure message
10
12
  ("expected [actual] to satisfy block") is not very descriptive
@@ -3,9 +3,11 @@ Feature: start_with matcher
3
3
  Use the `start_with` matcher to specify that a string or array starts with
4
4
  the expected characters or elements.
5
5
 
6
+ ```ruby
6
7
  "this string".should start_with("this")
7
8
  "this string".should_not start_with("that")
8
9
  [0,1,2].should start_with(0, 1)
10
+ ```
9
11
 
10
12
  Scenario: with a string
11
13
  Given a file named "example_spec.rb" with:
@@ -3,16 +3,22 @@ Feature: throw_symbol matcher
3
3
  The throw_symbol matcher is used to specify that a block of code
4
4
  throws a symbol. The most basic form passes if any symbol is thrown:
5
5
 
6
+ ```ruby
6
7
  expect { throw :foo }.to throw_symbol
8
+ ```
7
9
 
8
10
  You'll often want to specify that a particular symbol is thrown:
9
11
 
12
+ ```ruby
10
13
  expect { throw :foo }.to throw_symbol(:foo)
14
+ ```
11
15
 
12
16
  If you care about the additional argument given to throw, you can
13
17
  specify that as well:
14
18
 
19
+ ```ruby
15
20
  expect { throw :foo, 7 }.to throw_symbol(:foo, 7)
21
+ ```
16
22
 
17
23
  Scenario: basic usage
18
24
  Given a file named "throw_symbol_matcher_spec.rb" with:
@@ -2,18 +2,20 @@ Feature: specify types of objects
2
2
 
3
3
  rspec-expectations includes two matchers to specify types of objects:
4
4
 
5
- * obj.should be_kind_of(type): calls obj.kind_of?(type), which returns
5
+ * `obj.should be_kind_of(type)`: calls `obj.kind_of?(type)`, which returns
6
6
  true if type is in obj's class hierarchy or is a module and is
7
7
  included in a class in obj's class hierarchy.
8
- * obj.should be_instance_of(type): calls obj.instance_of?(type), which
8
+ * `obj.should be_instance_of(type)`: calls `obj.instance_of?(type)`, which
9
9
  returns true if and only if type if obj's class.
10
10
 
11
11
  Both of these matchers have aliases:
12
12
 
13
- * obj.should be_a_kind_of(type) # same as obj.should be_kind_of(type)
14
- * obj.should be_a(type) # same as obj.should be_kind_of(type)
15
- * obj.should be_an(type) # same as obj.should be_kind_of(type)
16
- * obj.should be_an_instance_of(type) # same as obj.should be_instance_of(type)
13
+ ```ruby
14
+ obj.should be_a_kind_of(type) # same as obj.should be_kind_of(type)
15
+ obj.should be_a(type) # same as obj.should be_kind_of(type)
16
+ obj.should be_an(type) # same as obj.should be_kind_of(type)
17
+ obj.should be_an_instance_of(type) # same as obj.should be_instance_of(type)
18
+ ```
17
19
 
18
20
  Scenario: be_(a_)kind_of matcher
19
21
  Given a file named "be_kind_of_matcher_spec.rb" with:
@@ -31,7 +31,7 @@ ADDITIONAL
31
31
  # is also defined in rspec-core, but we can't assume it's loaded since
32
32
  # rspec-expectations should be usable w/o rspec-core.
33
33
  def warn_deprecation(message)
34
- send :warn, message
34
+ warn(message)
35
35
  end
36
36
  end
37
37
  end
@@ -25,7 +25,13 @@ module RSpec
25
25
  # diff includes lines of context. Otherwise, we might print
26
26
  # redundant lines.
27
27
  if (context_lines > 0) and hunk.overlaps?(oldhunk)
28
- hunk.unshift(oldhunk)
28
+ if hunk.respond_to?(:merge)
29
+ # diff-lcs 1.2.x
30
+ hunk.merge(oldhunk)
31
+ else
32
+ # diff-lcs 1.1.3
33
+ hunk.unshift(oldhunk)
34
+ end
29
35
  else
30
36
  output << oldhunk.diff(format)
31
37
  end
@@ -42,13 +48,7 @@ module RSpec
42
48
  def diff_as_object(actual, expected)
43
49
  actual_as_string = object_to_string(actual)
44
50
  expected_as_string = object_to_string(expected)
45
- diff = diff_as_string(actual_as_string, expected_as_string)
46
-
47
- if diff.empty?
48
- "#{actual}.==(#{expected}) returned false even though the diff " \
49
- "between #{actual} and #{expected} is empty. Check the " \
50
- "implementation of #{actual}.==."
51
- else
51
+ if diff = diff_as_string(actual_as_string, expected_as_string)
52
52
  color_diff diff
53
53
  end
54
54
  end
@@ -22,8 +22,7 @@ module RSpec
22
22
  if actual && expected
23
23
  if all_strings?(actual, expected)
24
24
  if any_multiline_strings?(actual, expected)
25
- expected = expected.join(',') if Array === expected
26
- message << "\nDiff:" << differ.diff_as_string(actual, expected)
25
+ message << "\nDiff:" << differ.diff_as_string(coerce_to_string(actual), coerce_to_string(expected))
27
26
  end
28
27
  elsif no_procs?(actual, expected) && no_numbers?(actual, expected)
29
28
  message << "\nDiff:" << differ.diff_as_object(actual, expected)
@@ -44,12 +43,27 @@ module RSpec
44
43
  end
45
44
 
46
45
  def any_multiline_strings?(*args)
47
- all_strings?(*args) && args.any? {|a| a =~ /\n/}
46
+ all_strings?(*args) && args.flatten.any? { |a| multiline?(a) }
48
47
  end
49
48
 
50
49
  def no_numbers?(*args)
51
50
  args.flatten.none? {|a| Numeric === a}
52
51
  end
52
+
53
+ def coerce_to_string(string_or_array)
54
+ return string_or_array unless Array === string_or_array
55
+ string_or_array.join(',')
56
+ end
57
+
58
+ if String.method_defined?(:encoding)
59
+ def multiline?(string)
60
+ string.include?("\n".encode(string.encoding))
61
+ end
62
+ else
63
+ def multiline?(string)
64
+ string.include?("\n")
65
+ end
66
+ end
53
67
  end
54
68
  end
55
69
  end
@@ -113,6 +113,52 @@ module RSpec
113
113
  def expect_enabled?(syntax_host = ::RSpec::Matchers)
114
114
  syntax_host.method_defined?(:expect)
115
115
  end
116
+
117
+ # @api private
118
+ # Generates a positive expectation expression.
119
+ def positive_expression(target_expression, matcher_expression)
120
+ expression_generator.positive_expression(target_expression, matcher_expression)
121
+ end
122
+
123
+ # @api private
124
+ # Generates a negative expectation expression.
125
+ def negative_expression(target_expression, matcher_expression)
126
+ expression_generator.negative_expression(target_expression, matcher_expression)
127
+ end
128
+
129
+ # @api private
130
+ # Selects which expression generator to use based on the configured syntax.
131
+ def expression_generator
132
+ if expect_enabled?
133
+ ExpectExpressionGenerator
134
+ else
135
+ ShouldExpressionGenerator
136
+ end
137
+ end
138
+
139
+ # @api private
140
+ # Generates expectation expressions for the `should` syntax.
141
+ module ShouldExpressionGenerator
142
+ def self.positive_expression(target_expression, matcher_expression)
143
+ "#{target_expression}.should #{matcher_expression}"
144
+ end
145
+
146
+ def self.negative_expression(target_expression, matcher_expression)
147
+ "#{target_expression}.should_not #{matcher_expression}"
148
+ end
149
+ end
150
+
151
+ # @api private
152
+ # Generates expectation expressions for the `expect` syntax.
153
+ module ExpectExpressionGenerator
154
+ def self.positive_expression(target_expression, matcher_expression)
155
+ "expect(#{target_expression}).to #{matcher_expression}"
156
+ end
157
+
158
+ def self.negative_expression(target_expression, matcher_expression)
159
+ "expect(#{target_expression}).not_to #{matcher_expression}"
160
+ end
161
+ end
116
162
  end
117
163
  end
118
164
  end
@@ -2,7 +2,7 @@ module RSpec
2
2
  module Expectations
3
3
  # @private
4
4
  module Version
5
- STRING = '2.12.1'
5
+ STRING = '2.13.0'
6
6
  end
7
7
  end
8
8
  end
@@ -62,7 +62,7 @@ module RSpec
62
62
  "expected #{@actual.inspect} to evaluate to false"
63
63
  end
64
64
 
65
- [:==, :<, :<=, :>=, :>, :===].each do |operator|
65
+ [:==, :<, :<=, :>=, :>, :===, :=~].each do |operator|
66
66
  define_method operator do |operand|
67
67
  BeComparedTo.new(operand, operator)
68
68
  end
@@ -99,7 +99,7 @@ module RSpec
99
99
 
100
100
  def matches?(actual)
101
101
  @actual = actual
102
- @actual.send @operator, @expected
102
+ @actual.__send__ @operator, @expected
103
103
  end
104
104
 
105
105
  def failure_message_for_should
@@ -108,7 +108,7 @@ module RSpec
108
108
 
109
109
  def failure_message_for_should_not
110
110
  message = <<-MESSAGE
111
- 'should_not be #{@operator} #{@expected}' not only FAILED,
111
+ `#{negative_expectation_expression}` not only FAILED,
112
112
  it is a bit confusing.
113
113
  MESSAGE
114
114
 
@@ -120,6 +120,10 @@ it is a bit confusing.
120
120
  def description
121
121
  "be #{@operator} #{expected_to_sentence}#{args_to_sentence}"
122
122
  end
123
+
124
+ def negative_expectation_expression
125
+ Expectations::Syntax.negative_expression("actual", "be #{@operator} #{@expected}")
126
+ end
123
127
  end
124
128
 
125
129
  class BePredicate < Be
@@ -8,14 +8,23 @@ module RSpec
8
8
 
9
9
  def matches?(actual)
10
10
  @actual = actual
11
- raise needs_expected unless defined? @expected
11
+ raise needs_expected unless defined? @expected
12
12
  raise needs_subtractable unless @actual.respond_to? :-
13
- (@actual - @expected).abs <= @delta
13
+ (@actual - @expected).abs <= @tolerance
14
14
  end
15
15
  alias == matches?
16
16
 
17
17
  def of(expected)
18
- @expected = expected
18
+ @expected = expected
19
+ @tolerance = @delta
20
+ @unit = ''
21
+ self
22
+ end
23
+
24
+ def percent_of(expected)
25
+ @expected = expected
26
+ @tolerance = @delta * @expected / 100
27
+ @unit = '%'
19
28
  self
20
29
  end
21
30
 
@@ -28,7 +37,7 @@ module RSpec
28
37
  end
29
38
 
30
39
  def description
31
- "be within #{@delta} of #{@expected}"
40
+ "be within #{@delta}#{@unit} of #{@expected}"
32
41
  end
33
42
 
34
43
  private
@@ -28,7 +28,7 @@ MESSAGE
28
28
 
29
29
  def evaluate_value_proc
30
30
  case val = @value_proc.call
31
- when Enumerable
31
+ when Enumerable, String
32
32
  val.dup
33
33
  else
34
34
  val
@@ -72,7 +72,7 @@ MESSAGE
72
72
  def by_at_most(maximum)
73
73
  @maximum = maximum
74
74
  self
75
- end
75
+ end
76
76
 
77
77
  def to(to)
78
78
  @eval_after = true
@@ -14,7 +14,7 @@ expected #{inspect_object(expected)}
14
14
 
15
15
  Compared using equal?, which compares object identity,
16
16
  but expected and actual are not the same object. Use
17
- 'actual.should eq(expected)' if you don't care about
17
+ `#{eq_expression}` if you don't care about
18
18
  object identity in this example.
19
19
 
20
20
  MESSAGE
@@ -38,6 +38,10 @@ MESSAGE
38
38
  def inspect_object(o)
39
39
  "#<#{o.class}:#{o.object_id}> => #{o.inspect}"
40
40
  end
41
+
42
+ def eq_expression
43
+ Expectations::Syntax.positive_expression("actual", "eq(expected)")
44
+ end
41
45
  end
42
46
  end
43
47
  end
@@ -9,7 +9,7 @@ module RSpec
9
9
  def matches?(actual)
10
10
  @actual = actual
11
11
  predicates = [:exist?, :exists?].select { |p| @actual.respond_to?(p) }
12
- existence_values = predicates.map { |p| @actual.send(p, *@expected) }
12
+ existence_values = predicates.map { |p| @actual.__send__(p, *@expected) }
13
13
  uniq_truthy_values = existence_values.map { |v| !!v }.uniq
14
14
 
15
15
  case uniq_truthy_values.size
@@ -24,7 +24,7 @@ module RSpec
24
24
  collection = determine_collection(collection_or_owner)
25
25
  query_method = determine_query_method(collection)
26
26
  raise not_a_collection unless query_method
27
- @actual = collection.send(query_method)
27
+ @actual = collection.__send__(query_method)
28
28
  case @relativity
29
29
  when :at_least then @actual >= @expected
30
30
  when :at_most then @actual <= @expected
@@ -35,13 +35,13 @@ module RSpec
35
35
 
36
36
  def determine_collection(collection_or_owner)
37
37
  if collection_or_owner.respond_to?(@collection_name)
38
- collection_or_owner.send(@collection_name, *@args, &@block)
38
+ collection_or_owner.__send__(@collection_name, *@args, &@block)
39
39
  elsif (@plural_collection_name && collection_or_owner.respond_to?(@plural_collection_name))
40
- collection_or_owner.send(@plural_collection_name, *@args, &@block)
40
+ collection_or_owner.__send__(@plural_collection_name, *@args, &@block)
41
41
  elsif determine_query_method(collection_or_owner)
42
42
  collection_or_owner
43
43
  else
44
- collection_or_owner.send(@collection_name, *@args, &@block)
44
+ collection_or_owner.__send__(@collection_name, *@args, &@block)
45
45
  end
46
46
  end
47
47
 
@@ -64,17 +64,17 @@ module RSpec
64
64
  return <<-EOF
65
65
  Isn't life confusing enough?
66
66
  Instead of having to figure out the meaning of this:
67
- should_not have_at_most(#{@expected}).#{@collection_name}
67
+ #{Expectations::Syntax.negative_expression("actual", "have_at_most(#{@expected}).#{@collection_name}")}
68
68
  We recommend that you use this instead:
69
- should have_at_least(#{@expected + 1}).#{@collection_name}
69
+ #{Expectations::Syntax.positive_expression("actual", "have_at_least(#{@expected + 1}).#{@collection_name}")}
70
70
  EOF
71
71
  elsif @relativity == :at_least
72
72
  return <<-EOF
73
73
  Isn't life confusing enough?
74
74
  Instead of having to figure out the meaning of this:
75
- should_not have_at_least(#{@expected}).#{@collection_name}
75
+ #{Expectations::Syntax.negative_expression("actual", "have_at_least(#{@expected}).#{@collection_name}")}
76
76
  We recommend that you use this instead:
77
- should have_at_most(#{@expected - 1}).#{@collection_name}
77
+ #{Expectations::Syntax.positive_expression("actual", "have_at_most(#{@expected - 1}).#{@collection_name}")}
78
78
  EOF
79
79
  end
80
80
  end
@@ -21,19 +21,23 @@ module RSpec
21
21
  end
22
22
 
23
23
  def diffable?
24
- true
24
+ # Matchers do not diff well, since diff uses their inspect
25
+ # output, which includes their instance variables and such.
26
+ @expected.none? { |e| is_a_matcher?(e) }
25
27
  end
26
28
 
27
29
  private
28
30
 
29
31
  def perform_match(predicate, hash_predicate, actuals, expecteds)
30
- expecteds.send(predicate) do |expected|
32
+ expecteds.__send__(predicate) do |expected|
31
33
  if comparing_hash_values?(actuals, expected)
32
- expected.send(hash_predicate) {|k,v|
34
+ expected.__send__(hash_predicate) { |k,v|
33
35
  actuals.has_key?(k) && actuals[k] == v
34
36
  }
35
37
  elsif comparing_hash_keys?(actuals, expected)
36
38
  actuals.has_key?(expected)
39
+ elsif comparing_with_matcher?(actual, expected)
40
+ actual.any? { |value| expected.matches?(value) }
37
41
  else
38
42
  actuals.include?(expected)
39
43
  end
@@ -47,6 +51,18 @@ module RSpec
47
51
  def comparing_hash_values?(actual, expected)
48
52
  actual.is_a?(Hash) && expected.is_a?(Hash)
49
53
  end
54
+
55
+ def comparing_with_matcher?(actual, expected)
56
+ actual.is_a?(Array) && is_a_matcher?(expected)
57
+ end
58
+
59
+ def is_a_matcher?(object)
60
+ return false if object.respond_to?(:i_respond_to_everything_so_im_not_really_a_matcher)
61
+
62
+ [:failure_message_for_should, :failure_message].any? do |msg|
63
+ object.respond_to?(msg)
64
+ end && object.respond_to?(:matches?)
65
+ end
50
66
  end
51
67
  end
52
68
  end
@@ -42,7 +42,7 @@ module RSpec
42
42
 
43
43
  def find_failing_method_names(actual, filter_method)
44
44
  @actual = actual
45
- @failing_method_names = @names.send(filter_method) do |name|
45
+ @failing_method_names = @names.__send__(filter_method) do |name|
46
46
  @actual.respond_to?(name) && matches_arity?(actual, name)
47
47
  end
48
48
  end
@@ -28,7 +28,7 @@ module RSpec
28
28
  Thread.critical = orig_critical
29
29
  end
30
30
  begin
31
- return send(method_name, *args)
31
+ return __send__(method_name, *args)
32
32
  ensure
33
33
  singleton_class.module_eval{ remove_method(method_name) } rescue nil
34
34
  end