rspec-expectations 2.12.1 → 2.13.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +31 -0
- data/README.md +1 -1
- data/features/built_in_matchers/be.feature +6 -4
- data/features/built_in_matchers/be_within.feature +3 -1
- data/features/built_in_matchers/cover.feature +2 -0
- data/features/built_in_matchers/end_with.feature +2 -0
- data/features/built_in_matchers/equality.feature +9 -15
- data/features/built_in_matchers/exist.feature +2 -0
- data/features/built_in_matchers/expect_error.feature +14 -8
- data/features/built_in_matchers/have.feature +11 -5
- data/features/built_in_matchers/include.feature +53 -0
- data/features/built_in_matchers/match.feature +2 -0
- data/features/built_in_matchers/operators.feature +17 -11
- data/features/built_in_matchers/predicates.feature +21 -13
- data/features/built_in_matchers/respond_to.feature +7 -1
- data/features/built_in_matchers/satisfy.feature +2 -0
- data/features/built_in_matchers/start_with.feature +2 -0
- data/features/built_in_matchers/throw_symbol.feature +6 -0
- data/features/built_in_matchers/types.feature +8 -6
- data/lib/rspec/expectations/deprecation.rb +1 -1
- data/lib/rspec/expectations/differ.rb +8 -8
- data/lib/rspec/expectations/fail_with.rb +17 -3
- data/lib/rspec/expectations/syntax.rb +46 -0
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/matchers/built_in/be.rb +7 -3
- data/lib/rspec/matchers/built_in/be_within.rb +13 -4
- data/lib/rspec/matchers/built_in/change.rb +2 -2
- data/lib/rspec/matchers/built_in/equal.rb +5 -1
- data/lib/rspec/matchers/built_in/exist.rb +1 -1
- data/lib/rspec/matchers/built_in/have.rb +8 -8
- data/lib/rspec/matchers/built_in/include.rb +19 -3
- data/lib/rspec/matchers/built_in/respond_to.rb +1 -1
- data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +1 -1
- data/lib/rspec/matchers/matcher.rb +4 -3
- data/lib/rspec/matchers/operator_matcher.rb +1 -1
- data/lib/rspec/matchers/pretty.rb +5 -1
- data/spec/rspec/expectations/differ_spec.rb +8 -15
- data/spec/rspec/expectations/expectation_target_spec.rb +18 -8
- data/spec/rspec/expectations/extensions/kernel_spec.rb +15 -15
- data/spec/rspec/expectations/fail_with_spec.rb +41 -16
- data/spec/rspec/expectations/handler_spec.rb +13 -13
- data/spec/rspec/expectations/syntax_spec.rb +70 -8
- data/spec/rspec/matchers/base_matcher_spec.rb +14 -12
- data/spec/rspec/matchers/be_close_spec.rb +1 -1
- data/spec/rspec/matchers/be_instance_of_spec.rb +14 -8
- data/spec/rspec/matchers/be_kind_of_spec.rb +12 -8
- data/spec/rspec/matchers/be_spec.rb +212 -148
- data/spec/rspec/matchers/be_within_spec.rb +91 -42
- data/spec/rspec/matchers/change_spec.rb +52 -38
- data/spec/rspec/matchers/configuration_spec.rb +19 -15
- data/spec/rspec/matchers/cover_spec.rb +19 -19
- data/spec/rspec/matchers/description_generation_spec.rb +86 -86
- data/spec/rspec/matchers/dsl_spec.rb +7 -7
- data/spec/rspec/matchers/eq_spec.rb +17 -11
- data/spec/rspec/matchers/eql_spec.rb +10 -10
- data/spec/rspec/matchers/equal_spec.rb +27 -9
- data/spec/rspec/matchers/exist_spec.rb +35 -21
- data/spec/rspec/matchers/has_spec.rb +33 -29
- data/spec/rspec/matchers/have_spec.rb +165 -151
- data/spec/rspec/matchers/include_matcher_integration_spec.rb +30 -0
- data/spec/rspec/matchers/include_spec.rb +282 -124
- data/spec/rspec/matchers/match_array_spec.rb +90 -49
- data/spec/rspec/matchers/match_spec.rb +21 -21
- data/spec/rspec/matchers/matcher_spec.rb +85 -48
- data/spec/rspec/matchers/matchers_spec.rb +12 -6
- data/spec/rspec/matchers/method_missing_spec.rb +5 -1
- data/spec/rspec/matchers/operator_matcher_spec.rb +216 -237
- data/spec/rspec/matchers/raise_error_spec.rb +132 -132
- data/spec/rspec/matchers/respond_to_spec.rb +109 -112
- data/spec/rspec/matchers/satisfy_spec.rb +16 -16
- data/spec/rspec/matchers/start_with_end_with_spec.rb +36 -32
- data/spec/rspec/matchers/throw_symbol_spec.rb +24 -24
- data/spec/rspec/matchers/yield_spec.rb +7 -7
- data/spec/spec_helper.rb +46 -19
- data/spec/support/in_sub_process.rb +27 -20
- 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)
|
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)
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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:
|
@@ -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.
|
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
|
-
|
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
|
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
|
@@ -62,7 +62,7 @@ module RSpec
|
|
62
62
|
"expected #{@actual.inspect} to evaluate to false"
|
63
63
|
end
|
64
64
|
|
65
|
-
[:==, :<, :<=, :>=, :>,
|
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.
|
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
|
-
|
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 <= @
|
13
|
+
(@actual - @expected).abs <= @tolerance
|
14
14
|
end
|
15
15
|
alias == matches?
|
16
16
|
|
17
17
|
def of(expected)
|
18
|
-
@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
|
-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|
67
|
+
#{Expectations::Syntax.negative_expression("actual", "have_at_most(#{@expected}).#{@collection_name}")}
|
68
68
|
We recommend that you use this instead:
|
69
|
-
|
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
|
-
|
75
|
+
#{Expectations::Syntax.negative_expression("actual", "have_at_least(#{@expected}).#{@collection_name}")}
|
76
76
|
We recommend that you use this instead:
|
77
|
-
|
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
|
-
|
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.
|
32
|
+
expecteds.__send__(predicate) do |expected|
|
31
33
|
if comparing_hash_values?(actuals, expected)
|
32
|
-
expected.
|
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.
|
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
|