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.
- data.tar.gz.sig +2 -2
- data/.yardopts +1 -0
- data/Changelog.md +138 -0
- data/README.md +75 -8
- data/features/README.md +2 -2
- data/features/built_in_matchers/README.md +12 -9
- data/features/built_in_matchers/comparisons.feature +2 -2
- data/features/built_in_matchers/contain_exactly.feature +46 -0
- data/features/built_in_matchers/expect_change.feature +2 -2
- data/features/built_in_matchers/include.feature +0 -48
- data/features/built_in_matchers/output.feature +70 -0
- data/features/composing_matchers.feature +250 -0
- data/features/compound_expectations.feature +45 -0
- data/features/custom_matchers/access_running_example.feature +1 -1
- data/features/custom_matchers/define_matcher.feature +6 -6
- data/features/custom_matchers/define_matcher_outside_rspec.feature +4 -8
- data/features/test_frameworks/{test_unit.feature → minitest.feature} +11 -11
- data/lib/rspec/expectations.rb +31 -42
- data/lib/rspec/expectations/diff_presenter.rb +141 -0
- data/lib/rspec/expectations/differ.rb +22 -132
- data/lib/rspec/expectations/encoded_string.rb +56 -0
- data/lib/rspec/expectations/expectation_target.rb +0 -30
- data/lib/rspec/expectations/fail_with.rb +2 -2
- data/lib/rspec/expectations/handler.rb +128 -31
- data/lib/rspec/expectations/minitest_integration.rb +16 -0
- data/lib/rspec/expectations/syntax.rb +4 -58
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/matchers.rb +298 -60
- data/lib/rspec/matchers/aliased_matcher.rb +35 -0
- data/lib/rspec/matchers/built_in.rb +37 -33
- data/lib/rspec/matchers/built_in/base_matcher.rb +25 -15
- data/lib/rspec/matchers/built_in/be.rb +23 -31
- data/lib/rspec/matchers/built_in/be_between.rb +55 -0
- data/lib/rspec/matchers/built_in/be_within.rb +15 -11
- data/lib/rspec/matchers/built_in/change.rb +198 -81
- data/lib/rspec/matchers/built_in/compound.rb +106 -0
- data/lib/rspec/matchers/built_in/contain_exactly.rb +245 -0
- data/lib/rspec/matchers/built_in/eq.rb +43 -4
- data/lib/rspec/matchers/built_in/eql.rb +2 -2
- data/lib/rspec/matchers/built_in/equal.rb +35 -18
- data/lib/rspec/matchers/built_in/has.rb +16 -15
- data/lib/rspec/matchers/built_in/include.rb +45 -23
- data/lib/rspec/matchers/built_in/match.rb +6 -3
- data/lib/rspec/matchers/built_in/operators.rb +103 -0
- data/lib/rspec/matchers/built_in/output.rb +108 -0
- data/lib/rspec/matchers/built_in/raise_error.rb +9 -15
- data/lib/rspec/matchers/built_in/respond_to.rb +5 -4
- data/lib/rspec/matchers/built_in/satisfy.rb +4 -3
- data/lib/rspec/matchers/built_in/start_and_end_with.rb +37 -16
- data/lib/rspec/matchers/built_in/throw_symbol.rb +6 -5
- data/lib/rspec/matchers/built_in/yield.rb +31 -29
- data/lib/rspec/matchers/composable.rb +138 -0
- data/lib/rspec/matchers/dsl.rb +330 -0
- data/lib/rspec/matchers/generated_descriptions.rb +6 -6
- data/lib/rspec/matchers/matcher_delegator.rb +33 -0
- data/lib/rspec/matchers/pretty.rb +13 -2
- data/spec/rspec/expectations/{differ_spec.rb → diff_presenter_spec.rb} +56 -36
- data/spec/rspec/expectations/encoded_string_spec.rb +74 -0
- data/spec/rspec/expectations/extensions/kernel_spec.rb +11 -11
- data/spec/rspec/expectations/fail_with_spec.rb +8 -8
- data/spec/rspec/expectations/handler_spec.rb +27 -49
- data/spec/rspec/expectations/minitest_integration_spec.rb +27 -0
- data/spec/rspec/expectations/syntax_spec.rb +17 -67
- data/spec/rspec/expectations_spec.rb +7 -52
- data/spec/rspec/matchers/aliased_matcher_spec.rb +48 -0
- data/spec/rspec/matchers/aliases_spec.rb +449 -0
- data/spec/rspec/matchers/{base_matcher_spec.rb → built_in/base_matcher_spec.rb} +24 -3
- data/spec/rspec/matchers/built_in/be_between_spec.rb +159 -0
- data/spec/rspec/matchers/{be_instance_of_spec.rb → built_in/be_instance_of_spec.rb} +0 -0
- data/spec/rspec/matchers/{be_kind_of_spec.rb → built_in/be_kind_of_spec.rb} +0 -0
- data/spec/rspec/matchers/{be_spec.rb → built_in/be_spec.rb} +76 -32
- data/spec/rspec/matchers/{be_within_spec.rb → built_in/be_within_spec.rb} +6 -2
- data/spec/rspec/matchers/{change_spec.rb → built_in/change_spec.rb} +310 -69
- data/spec/rspec/matchers/built_in/compound_spec.rb +292 -0
- data/spec/rspec/matchers/built_in/contain_exactly_spec.rb +441 -0
- data/spec/rspec/matchers/{cover_spec.rb → built_in/cover_spec.rb} +0 -0
- data/spec/rspec/matchers/built_in/eq_spec.rb +156 -0
- data/spec/rspec/matchers/{eql_spec.rb → built_in/eql_spec.rb} +2 -2
- data/spec/rspec/matchers/built_in/equal_spec.rb +106 -0
- data/spec/rspec/matchers/{exist_spec.rb → built_in/exist_spec.rb} +1 -1
- data/spec/rspec/matchers/{has_spec.rb → built_in/has_spec.rb} +39 -0
- data/spec/rspec/matchers/{include_spec.rb → built_in/include_spec.rb} +118 -109
- data/spec/rspec/matchers/{match_spec.rb → built_in/match_spec.rb} +30 -2
- data/spec/rspec/matchers/{operator_matcher_spec.rb → built_in/operators_spec.rb} +26 -26
- data/spec/rspec/matchers/built_in/output_spec.rb +165 -0
- data/spec/rspec/matchers/{raise_error_spec.rb → built_in/raise_error_spec.rb} +81 -11
- data/spec/rspec/matchers/{respond_to_spec.rb → built_in/respond_to_spec.rb} +0 -0
- data/spec/rspec/matchers/{satisfy_spec.rb → built_in/satisfy_spec.rb} +0 -0
- data/spec/rspec/matchers/{start_with_end_with_spec.rb → built_in/start_and_end_with_spec.rb} +82 -15
- data/spec/rspec/matchers/{throw_symbol_spec.rb → built_in/throw_symbol_spec.rb} +29 -10
- data/spec/rspec/matchers/{yield_spec.rb → built_in/yield_spec.rb} +90 -0
- data/spec/rspec/matchers/configuration_spec.rb +7 -39
- data/spec/rspec/matchers/description_generation_spec.rb +22 -6
- data/spec/rspec/matchers/dsl_spec.rb +838 -0
- data/spec/rspec/matchers/legacy_spec.rb +101 -0
- data/spec/rspec/matchers_spec.rb +74 -0
- data/spec/spec_helper.rb +35 -21
- data/spec/support/shared_examples.rb +26 -4
- metadata +172 -116
- metadata.gz.sig +3 -4
- checksums.yaml +0 -15
- checksums.yaml.gz.sig +0 -0
- data/features/built_in_matchers/match_array.feature +0 -37
- data/lib/rspec/expectations/errors.rb +0 -9
- data/lib/rspec/expectations/extensions.rb +0 -1
- data/lib/rspec/expectations/extensions/object.rb +0 -29
- data/lib/rspec/matchers/built_in/match_array.rb +0 -51
- data/lib/rspec/matchers/compatibility.rb +0 -14
- data/lib/rspec/matchers/matcher.rb +0 -301
- data/lib/rspec/matchers/method_missing.rb +0 -12
- data/lib/rspec/matchers/operator_matcher.rb +0 -99
- data/lib/rspec/matchers/test_unit_integration.rb +0 -11
- data/spec/rspec/matchers/eq_spec.rb +0 -60
- data/spec/rspec/matchers/equal_spec.rb +0 -78
- data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
- data/spec/rspec/matchers/match_array_spec.rb +0 -194
- data/spec/rspec/matchers/matcher_spec.rb +0 -706
- data/spec/rspec/matchers/matchers_spec.rb +0 -36
- data/spec/rspec/matchers/method_missing_spec.rb +0 -28
- data/spec/support/classes.rb +0 -56
- data/spec/support/in_sub_process.rb +0 -37
- data/spec/support/ruby_version.rb +0 -10
@@ -0,0 +1,35 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
# Decorator that wraps a matcher and overrides `description`
|
4
|
+
# using the provided block in order to support an alias
|
5
|
+
# of a matcher. This is intended for use when composing
|
6
|
+
# matchers, so that you can use an expression like
|
7
|
+
# `include( a_value_within(0.1).of(3) )` rather than
|
8
|
+
# `include( be_within(0.1).of(3) )`, and have the corresponding
|
9
|
+
# description read naturally.
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class AliasedMatcher < MatcherDelegator
|
13
|
+
def initialize(base_matcher, description_block)
|
14
|
+
@description_block = description_block
|
15
|
+
super(base_matcher)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Forward messages on to the wrapped matcher.
|
19
|
+
# Since many matchers provide a fluent interface
|
20
|
+
# (e.g. `a_value_within(0.1).of(3)`), we need to wrap
|
21
|
+
# the returned value if it responds to `description`,
|
22
|
+
# so that our override can be applied when it is eventually
|
23
|
+
# used.
|
24
|
+
def method_missing(*)
|
25
|
+
return_val = super
|
26
|
+
return return_val unless return_val.respond_to?(:description)
|
27
|
+
AliasedMatcher.new(return_val, @description_block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def description
|
31
|
+
@description_block.call(super)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -1,39 +1,43 @@
|
|
1
|
+
require 'rspec/matchers/built_in/base_matcher'
|
2
|
+
|
1
3
|
module RSpec
|
2
4
|
module Matchers
|
3
5
|
module BuiltIn
|
4
|
-
|
5
|
-
autoload :BeAnInstanceOf,
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
autoload :
|
9
|
-
autoload :
|
10
|
-
autoload :
|
11
|
-
autoload :BePredicate,
|
12
|
-
autoload :
|
13
|
-
autoload :BeWithin,
|
14
|
-
autoload :Change,
|
15
|
-
autoload :
|
16
|
-
autoload :
|
17
|
-
autoload :
|
18
|
-
autoload :
|
19
|
-
autoload :
|
20
|
-
autoload :
|
21
|
-
autoload :
|
22
|
-
autoload :
|
23
|
-
autoload :
|
24
|
-
autoload :
|
25
|
-
autoload :
|
26
|
-
autoload :
|
27
|
-
autoload :
|
28
|
-
autoload :
|
29
|
-
autoload :
|
30
|
-
autoload :
|
31
|
-
autoload :
|
32
|
-
autoload :
|
33
|
-
autoload :
|
34
|
-
autoload :
|
6
|
+
autoload :BeAKindOf, 'rspec/matchers/built_in/be_kind_of'
|
7
|
+
autoload :BeAnInstanceOf, 'rspec/matchers/built_in/be_instance_of'
|
8
|
+
autoload :BeBetween, 'rspec/matchers/built_in/be_between'
|
9
|
+
autoload :Be, 'rspec/matchers/built_in/be'
|
10
|
+
autoload :BeComparedTo, 'rspec/matchers/built_in/be'
|
11
|
+
autoload :BeFalsey, 'rspec/matchers/built_in/be'
|
12
|
+
autoload :BeNil, 'rspec/matchers/built_in/be'
|
13
|
+
autoload :BePredicate, 'rspec/matchers/built_in/be'
|
14
|
+
autoload :BeTruthy, 'rspec/matchers/built_in/be'
|
15
|
+
autoload :BeWithin, 'rspec/matchers/built_in/be_within'
|
16
|
+
autoload :Change, 'rspec/matchers/built_in/change'
|
17
|
+
autoload :Compound, 'rspec/matchers/built_in/compound'
|
18
|
+
autoload :ContainExactly, 'rspec/matchers/built_in/contain_exactly'
|
19
|
+
autoload :Cover, 'rspec/matchers/built_in/cover'
|
20
|
+
autoload :EndWith, 'rspec/matchers/built_in/start_and_end_with'
|
21
|
+
autoload :Eq, 'rspec/matchers/built_in/eq'
|
22
|
+
autoload :Eql, 'rspec/matchers/built_in/eql'
|
23
|
+
autoload :Equal, 'rspec/matchers/built_in/equal'
|
24
|
+
autoload :Exist, 'rspec/matchers/built_in/exist'
|
25
|
+
autoload :Has, 'rspec/matchers/built_in/has'
|
26
|
+
autoload :Include, 'rspec/matchers/built_in/include'
|
27
|
+
autoload :Match, 'rspec/matchers/built_in/match'
|
28
|
+
autoload :NegativeOperatorMatcher, 'rspec/matchers/built_in/operators'
|
29
|
+
autoload :OperatorMatcher, 'rspec/matchers/built_in/operators'
|
30
|
+
autoload :Output, 'rspec/matchers/built_in/output'
|
31
|
+
autoload :PositiveOperatorMatcher, 'rspec/matchers/built_in/operators'
|
32
|
+
autoload :RaiseError, 'rspec/matchers/built_in/raise_error'
|
33
|
+
autoload :RespondTo, 'rspec/matchers/built_in/respond_to'
|
34
|
+
autoload :Satisfy, 'rspec/matchers/built_in/satisfy'
|
35
|
+
autoload :StartWith, 'rspec/matchers/built_in/start_and_end_with'
|
36
|
+
autoload :ThrowSymbol, 'rspec/matchers/built_in/throw_symbol'
|
37
|
+
autoload :YieldControl, 'rspec/matchers/built_in/yield'
|
38
|
+
autoload :YieldSuccessiveArgs, 'rspec/matchers/built_in/yield'
|
39
|
+
autoload :YieldWithArgs, 'rspec/matchers/built_in/yield'
|
40
|
+
autoload :YieldWithNoArgs, 'rspec/matchers/built_in/yield'
|
35
41
|
end
|
36
42
|
end
|
37
43
|
end
|
38
|
-
|
39
|
-
|
@@ -13,11 +13,14 @@ module RSpec
|
|
13
13
|
# class. If/when this changes, we will announce it and remove this warning.
|
14
14
|
class BaseMatcher
|
15
15
|
include RSpec::Matchers::Pretty
|
16
|
+
include RSpec::Matchers::Composable
|
17
|
+
|
18
|
+
UNDEFINED = Object.new.freeze
|
16
19
|
|
17
20
|
attr_reader :actual, :expected, :rescued_exception
|
18
21
|
|
19
|
-
def initialize(expected =
|
20
|
-
@expected = expected
|
22
|
+
def initialize(expected = UNDEFINED)
|
23
|
+
@expected = expected unless UNDEFINED.equal?(expected)
|
21
24
|
end
|
22
25
|
|
23
26
|
def matches?(actual)
|
@@ -35,32 +38,39 @@ module RSpec
|
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
38
|
-
def
|
39
|
-
assert_ivars :@actual
|
40
|
-
"expected #{@actual.inspect} to #{
|
41
|
+
def failure_message
|
42
|
+
assert_ivars :@actual
|
43
|
+
"expected #{@actual.inspect} to #{description}"
|
41
44
|
end
|
42
45
|
|
43
|
-
def
|
44
|
-
assert_ivars :@actual
|
45
|
-
"expected #{@actual.inspect} not to #{
|
46
|
+
def failure_message_when_negated
|
47
|
+
assert_ivars :@actual
|
48
|
+
"expected #{@actual.inspect} not to #{description}"
|
46
49
|
end
|
47
50
|
|
48
51
|
def description
|
49
|
-
|
52
|
+
return name_to_sentence unless defined?(@expected)
|
53
|
+
"#{name_to_sentence}#{to_sentence @expected}"
|
50
54
|
end
|
51
55
|
|
52
56
|
def diffable?
|
53
57
|
false
|
54
58
|
end
|
55
59
|
|
56
|
-
|
57
|
-
matches?(other)
|
58
|
-
end
|
60
|
+
private
|
59
61
|
|
60
|
-
|
62
|
+
def assert_ivars(*expected_ivars)
|
63
|
+
if (expected_ivars - present_ivars).any?
|
64
|
+
raise "#{self.class.name} needs to supply#{to_sentence expected_ivars}"
|
65
|
+
end
|
66
|
+
end
|
61
67
|
|
62
|
-
|
63
|
-
|
68
|
+
if RUBY_VERSION.to_f < 1.9
|
69
|
+
def present_ivars
|
70
|
+
instance_variables.map { |v| v.to_sym }
|
71
|
+
end
|
72
|
+
else
|
73
|
+
alias present_ivars instance_variables
|
64
74
|
end
|
65
75
|
end
|
66
76
|
end
|
@@ -8,11 +8,11 @@ module RSpec
|
|
8
8
|
!!actual
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
11
|
+
def failure_message
|
12
12
|
"expected: truthy value\n got: #{actual.inspect}"
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def failure_message_when_negated
|
16
16
|
"expected: falsey value\n got: #{actual.inspect}"
|
17
17
|
end
|
18
18
|
end
|
@@ -22,11 +22,11 @@ module RSpec
|
|
22
22
|
!actual
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
25
|
+
def failure_message
|
26
26
|
"expected: falsey value\n got: #{actual.inspect}"
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
29
|
+
def failure_message_when_negated
|
30
30
|
"expected: truthy value\n got: #{actual.inspect}"
|
31
31
|
end
|
32
32
|
end
|
@@ -36,11 +36,11 @@ module RSpec
|
|
36
36
|
actual.nil?
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
39
|
+
def failure_message
|
40
40
|
"expected: nil\n got: #{actual.inspect}"
|
41
41
|
end
|
42
42
|
|
43
|
-
def
|
43
|
+
def failure_message_when_negated
|
44
44
|
"expected: not nil\n got: nil"
|
45
45
|
end
|
46
46
|
end
|
@@ -80,11 +80,11 @@ module RSpec
|
|
80
80
|
!!actual
|
81
81
|
end
|
82
82
|
|
83
|
-
def
|
83
|
+
def failure_message
|
84
84
|
"expected #{@actual.inspect} to evaluate to true"
|
85
85
|
end
|
86
86
|
|
87
|
-
def
|
87
|
+
def failure_message_when_negated
|
88
88
|
"expected #{@actual.inspect} to evaluate to false"
|
89
89
|
end
|
90
90
|
|
@@ -95,10 +95,12 @@ module RSpec
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
-
class BeComparedTo <
|
98
|
+
class BeComparedTo < BaseMatcher
|
99
|
+
include BeHelpers
|
100
|
+
|
99
101
|
def initialize(operand, operator)
|
100
102
|
@expected, @operator = operand, operator
|
101
|
-
|
103
|
+
@args = []
|
102
104
|
end
|
103
105
|
|
104
106
|
def matches?(actual)
|
@@ -106,28 +108,22 @@ module RSpec
|
|
106
108
|
@actual.__send__ @operator, @expected
|
107
109
|
end
|
108
110
|
|
109
|
-
def
|
111
|
+
def failure_message
|
110
112
|
"expected: #{@operator} #{@expected.inspect}\n got: #{@operator.to_s.gsub(/./, ' ')} #{@actual.inspect}"
|
111
113
|
end
|
112
114
|
|
113
|
-
def
|
114
|
-
message =
|
115
|
-
|
116
|
-
it is a bit confusing.
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
"It might be more clearly expressed without the \"be\"?" :
|
121
|
-
"It might be more clearly expressed in the positive?")
|
115
|
+
def failure_message_when_negated
|
116
|
+
message = "`expect(#{@actual.inspect}).not_to be #{@operator} #{@expected.inspect}`"
|
117
|
+
if [:<, :>, :<=, :>=].include?(@operator)
|
118
|
+
message + " not only FAILED, it is a bit confusing."
|
119
|
+
else
|
120
|
+
message
|
121
|
+
end
|
122
122
|
end
|
123
123
|
|
124
124
|
def description
|
125
125
|
"be #{@operator} #{expected_to_sentence}#{args_to_sentence}"
|
126
126
|
end
|
127
|
-
|
128
|
-
def negative_expectation_expression
|
129
|
-
Expectations::Syntax.negative_expression("actual", "be #{@operator} #{@expected}")
|
130
|
-
end
|
131
127
|
end
|
132
128
|
|
133
129
|
class BePredicate < BaseMatcher
|
@@ -149,7 +145,6 @@ it is a bit confusing.
|
|
149
145
|
begin
|
150
146
|
return @result = actual.__send__(predicate, *@args, &@block)
|
151
147
|
rescue NameError => predicate_missing_error
|
152
|
-
"this needs to be here or rcov will not count this branch even though it's executed in a code example"
|
153
148
|
end
|
154
149
|
|
155
150
|
begin
|
@@ -159,13 +154,11 @@ it is a bit confusing.
|
|
159
154
|
end
|
160
155
|
end
|
161
156
|
|
162
|
-
|
163
|
-
|
164
|
-
def failure_message_for_should
|
157
|
+
def failure_message
|
165
158
|
"expected #{predicate}#{args_to_s} to return true, got #{@result.inspect}"
|
166
159
|
end
|
167
160
|
|
168
|
-
def
|
161
|
+
def failure_message_when_negated
|
169
162
|
"expected #{predicate}#{args_to_s} to return false, got #{@result.inspect}"
|
170
163
|
end
|
171
164
|
|
@@ -200,8 +193,7 @@ it is a bit confusing.
|
|
200
193
|
end
|
201
194
|
|
202
195
|
def prefix_and_expected(symbol)
|
203
|
-
symbol.to_s
|
204
|
-
return $1, $3
|
196
|
+
Matchers::BE_PREDICATE_REGEX.match(symbol.to_s).captures.compact
|
205
197
|
end
|
206
198
|
|
207
199
|
def prefix_to_sentence
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
module BuiltIn
|
4
|
+
class BeBetween < BaseMatcher
|
5
|
+
def initialize(min, max)
|
6
|
+
@min, @max = min, max
|
7
|
+
inclusive
|
8
|
+
end
|
9
|
+
|
10
|
+
def inclusive
|
11
|
+
@less_than_operator = :<=
|
12
|
+
@greater_than_operator = :>=
|
13
|
+
@mode = :inclusive
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def exclusive
|
18
|
+
@less_than_operator = :<
|
19
|
+
@greater_than_operator = :>
|
20
|
+
@mode = :exclusive
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def matches?(actual)
|
25
|
+
@actual = actual
|
26
|
+
comparable? && compare
|
27
|
+
rescue ArgumentError
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def failure_message
|
32
|
+
"#{super}#{not_comparable_clause}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def description
|
36
|
+
"be between #{@min.inspect} and #{@max.inspect} (#{@mode})"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def comparable?
|
42
|
+
@actual.respond_to?(@less_than_operator) && @actual.respond_to?(@greater_than_operator)
|
43
|
+
end
|
44
|
+
|
45
|
+
def not_comparable_clause
|
46
|
+
", but it does not respond to `#{@less_than_operator}` and `#{@greater_than_operator}`" unless comparable?
|
47
|
+
end
|
48
|
+
|
49
|
+
def compare
|
50
|
+
@actual.__send__(@greater_than_operator, @min) && @actual.__send__(@less_than_operator, @max)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -2,17 +2,17 @@ module RSpec
|
|
2
2
|
module Matchers
|
3
3
|
module BuiltIn
|
4
4
|
class BeWithin
|
5
|
+
include Composable
|
6
|
+
|
5
7
|
def initialize(delta)
|
6
8
|
@delta = delta
|
7
9
|
end
|
8
10
|
|
9
11
|
def matches?(actual)
|
10
12
|
@actual = actual
|
11
|
-
raise needs_expected
|
12
|
-
|
13
|
-
(@actual - @expected).abs <= @tolerance
|
13
|
+
raise needs_expected unless defined? @expected
|
14
|
+
numeric? && (@actual - @expected).abs <= @tolerance
|
14
15
|
end
|
15
|
-
alias == matches?
|
16
16
|
|
17
17
|
def of(expected)
|
18
18
|
@expected = expected
|
@@ -28,27 +28,31 @@ module RSpec
|
|
28
28
|
self
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
32
|
-
"expected #{@actual} to #{description}"
|
31
|
+
def failure_message
|
32
|
+
"expected #{@actual.inspect} to #{description}#{not_numeric_clause}"
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
36
|
-
"expected #{@actual} not to #{description}"
|
35
|
+
def failure_message_when_negated
|
36
|
+
"expected #{@actual.inspect} not to #{description}"
|
37
37
|
end
|
38
38
|
|
39
39
|
def description
|
40
40
|
"be within #{@delta}#{@unit} of #{@expected}"
|
41
41
|
end
|
42
42
|
|
43
|
-
|
43
|
+
private
|
44
44
|
|
45
|
-
def
|
46
|
-
|
45
|
+
def numeric?
|
46
|
+
@actual.respond_to?(:-)
|
47
47
|
end
|
48
48
|
|
49
49
|
def needs_expected
|
50
50
|
ArgumentError.new "You must set an expected value using #of: be_within(#{@delta}).of(expected_value)"
|
51
51
|
end
|
52
|
+
|
53
|
+
def not_numeric_clause
|
54
|
+
", but it could not be treated as a numeric value" unless numeric?
|
55
|
+
end
|
52
56
|
end
|
53
57
|
end
|
54
58
|
end
|
@@ -1,139 +1,256 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Matchers
|
3
3
|
module BuiltIn
|
4
|
+
# Describes an expected mutation.
|
4
5
|
class Change
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@
|
6
|
+
include Composable
|
7
|
+
|
8
|
+
# Specifies the delta of the expected change.
|
9
|
+
def by(expected_delta)
|
10
|
+
ChangeRelatively.new(@change_details, expected_delta, :by) do |actual_delta|
|
11
|
+
values_match?(expected_delta, actual_delta)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Specifies a minimum delta of the expected change.
|
16
|
+
def by_at_least(minimum)
|
17
|
+
ChangeRelatively.new(@change_details, minimum, :by_at_least) do |actual_delta|
|
18
|
+
actual_delta >= minimum
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Specifies a maximum delta of the expected change.
|
23
|
+
def by_at_most(maximum)
|
24
|
+
ChangeRelatively.new(@change_details, maximum, :by_at_most) do |actual_delta|
|
25
|
+
actual_delta <= maximum
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Specifies the new value you expect.
|
30
|
+
def to(value)
|
31
|
+
ChangeToValue.new(@change_details, value)
|
10
32
|
end
|
11
33
|
|
34
|
+
# Specifies the original value.
|
35
|
+
def from(value)
|
36
|
+
ChangeFromValue.new(@change_details, value)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @api private
|
12
40
|
def matches?(event_proc)
|
13
41
|
raise_block_syntax_error if block_given?
|
42
|
+
@change_details.perform_change(event_proc)
|
43
|
+
@change_details.changed?
|
44
|
+
end
|
14
45
|
|
15
|
-
|
16
|
-
|
17
|
-
@
|
46
|
+
# @api private
|
47
|
+
def failure_message
|
48
|
+
"expected #{@change_details.message} to have changed, but is still #{description_of @change_details.actual_before}"
|
49
|
+
end
|
18
50
|
|
19
|
-
|
51
|
+
# @api private
|
52
|
+
def failure_message_when_negated
|
53
|
+
"expected #{@change_details.message} not to have changed, but did change from #{description_of @change_details.actual_before} to #{description_of @change_details.actual_after}"
|
20
54
|
end
|
21
|
-
alias == matches?
|
22
55
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
MESSAGE
|
56
|
+
# @api private
|
57
|
+
def description
|
58
|
+
"change #{@change_details.message}"
|
27
59
|
end
|
28
60
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
else
|
34
|
-
val
|
35
|
-
end
|
61
|
+
private
|
62
|
+
|
63
|
+
def initialize(receiver=nil, message=nil, &block)
|
64
|
+
@change_details = ChangeDetails.new(receiver, message, &block)
|
36
65
|
end
|
37
66
|
|
38
|
-
def
|
39
|
-
|
40
|
-
"
|
41
|
-
elsif @eval_after && !expected_matches_actual?(@expected_after, @actual_after)
|
42
|
-
"#{message} should have been changed to #{failure_message_for_expected_after}, but is now #{@actual_after.inspect}"
|
43
|
-
elsif @expected_delta
|
44
|
-
"#{message} should have been changed by #{@expected_delta.inspect}, but was changed by #{actual_delta.inspect}"
|
45
|
-
elsif @minimum
|
46
|
-
"#{message} should have been changed by at least #{@minimum.inspect}, but was changed by #{actual_delta.inspect}"
|
47
|
-
elsif @maximum
|
48
|
-
"#{message} should have been changed by at most #{@maximum.inspect}, but was changed by #{actual_delta.inspect}"
|
49
|
-
else
|
50
|
-
"#{message} should have changed, but is still #{@actual_before.inspect}"
|
51
|
-
end
|
67
|
+
def raise_block_syntax_error
|
68
|
+
raise SyntaxError,
|
69
|
+
"The block passed to the `change` matcher must use `{ ... }` instead of do/end"
|
52
70
|
end
|
71
|
+
end
|
53
72
|
|
54
|
-
|
55
|
-
|
73
|
+
# Used to specify a relative change.
|
74
|
+
# @api private
|
75
|
+
class ChangeRelatively
|
76
|
+
include Composable
|
77
|
+
|
78
|
+
def initialize(change_details, expected_delta, relativity, &comparer)
|
79
|
+
@change_details = change_details
|
80
|
+
@expected_delta = expected_delta
|
81
|
+
@relativity = relativity
|
82
|
+
@comparer = comparer
|
56
83
|
end
|
57
84
|
|
58
|
-
def
|
59
|
-
"#{message}
|
85
|
+
def failure_message
|
86
|
+
"expected #{@change_details.message} to have changed #{@relativity.to_s.gsub("_", " ")} #{description_of @expected_delta}, " +
|
87
|
+
"but was changed by #{description_of @change_details.actual_delta}"
|
60
88
|
end
|
61
89
|
|
62
|
-
def
|
63
|
-
@
|
64
|
-
|
90
|
+
def matches?(event_proc)
|
91
|
+
@change_details.perform_change(event_proc)
|
92
|
+
@comparer.call(@change_details.actual_delta)
|
65
93
|
end
|
66
94
|
|
67
|
-
def
|
68
|
-
@
|
69
|
-
self
|
95
|
+
def does_not_match?(event_proc)
|
96
|
+
raise NotImplementedError, "`expect { }.not_to change { }.#{@relativity}()` is not supported"
|
70
97
|
end
|
71
98
|
|
72
|
-
|
73
|
-
|
74
|
-
|
99
|
+
# @api private
|
100
|
+
def description
|
101
|
+
"change #{@change_details.message} #{@relativity.to_s.gsub("_", " ")} #{description_of @expected_delta}"
|
75
102
|
end
|
103
|
+
end
|
76
104
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
105
|
+
# Base class for specifying a change from and/or to specific values.
|
106
|
+
# @api private
|
107
|
+
class SpecificValuesChange
|
108
|
+
include Composable
|
109
|
+
MATCH_ANYTHING = ::Object.ancestors.last
|
110
|
+
|
111
|
+
def initialize(change_details, from, to)
|
112
|
+
@change_details = change_details
|
113
|
+
@expected_before = from
|
114
|
+
@expected_after = to
|
81
115
|
end
|
82
116
|
|
83
|
-
def
|
84
|
-
@
|
85
|
-
@
|
86
|
-
self
|
117
|
+
def matches?(event_proc)
|
118
|
+
@change_details.perform_change(event_proc)
|
119
|
+
@change_details.changed? && matches_before? && matches_after?
|
87
120
|
end
|
88
121
|
|
89
122
|
def description
|
90
|
-
"change
|
123
|
+
"change #{@change_details.message} #{change_description}"
|
91
124
|
end
|
92
125
|
|
93
|
-
|
126
|
+
def failure_message
|
127
|
+
return before_value_failure unless matches_before?
|
128
|
+
return did_not_change_failure unless @change_details.changed?
|
129
|
+
after_value_failure
|
130
|
+
end
|
94
131
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
132
|
+
private
|
133
|
+
|
134
|
+
def matches_before?
|
135
|
+
values_match?(@expected_before, @change_details.actual_before)
|
136
|
+
end
|
137
|
+
|
138
|
+
def matches_after?
|
139
|
+
values_match?(@expected_after, @change_details.actual_after)
|
140
|
+
end
|
141
|
+
|
142
|
+
def before_value_failure
|
143
|
+
"expected #{@change_details.message} to have initially been #{description_of @expected_before}, but was #{description_of @change_details.actual_before}"
|
144
|
+
end
|
145
|
+
|
146
|
+
def after_value_failure
|
147
|
+
"expected #{@change_details.message} to have changed to #{description_of @expected_after}, but is now #{description_of @change_details.actual_after}"
|
148
|
+
end
|
149
|
+
|
150
|
+
def did_not_change_failure
|
151
|
+
"expected #{@change_details.message} to have changed #{change_description}, but did not change"
|
152
|
+
end
|
153
|
+
|
154
|
+
def did_change_failure
|
155
|
+
"expected #{@change_details.message} not to have changed, but did change from #{description_of @change_details.actual_before} to #{description_of @change_details.actual_after}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Used to specify a change from a specific value
|
160
|
+
# (and, optionally, to a specific value).
|
161
|
+
# @api private
|
162
|
+
class ChangeFromValue < SpecificValuesChange
|
163
|
+
def initialize(change_details, expected_before)
|
164
|
+
@description_suffix = nil
|
165
|
+
super(change_details, expected_before, MATCH_ANYTHING)
|
166
|
+
end
|
167
|
+
|
168
|
+
def to(value)
|
169
|
+
@expected_after = value
|
170
|
+
@description_suffix = " to #{description_of value}"
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
def does_not_match?(event_proc)
|
175
|
+
if @description_suffix
|
176
|
+
raise NotImplementedError, "`expect { }.not_to change { }.to()` is not supported"
|
100
177
|
end
|
178
|
+
|
179
|
+
@change_details.perform_change(event_proc)
|
180
|
+
!@change_details.changed? && matches_before?
|
101
181
|
end
|
102
182
|
|
103
|
-
def
|
104
|
-
|
183
|
+
def failure_message_when_negated
|
184
|
+
return before_value_failure unless matches_before?
|
185
|
+
did_change_failure
|
105
186
|
end
|
106
187
|
|
107
|
-
|
108
|
-
|
188
|
+
private
|
189
|
+
|
190
|
+
def change_description
|
191
|
+
"from #{description_of @expected_before}#{@description_suffix}"
|
109
192
|
end
|
193
|
+
end
|
110
194
|
|
111
|
-
|
112
|
-
|
195
|
+
# Used to specify a change to a specific value
|
196
|
+
# (and, optionally, from a specific value).
|
197
|
+
# @api private
|
198
|
+
class ChangeToValue < SpecificValuesChange
|
199
|
+
def initialize(change_details, expected_after)
|
200
|
+
@description_suffix = nil
|
201
|
+
super(change_details, MATCH_ANYTHING, expected_after)
|
113
202
|
end
|
114
203
|
|
115
|
-
def
|
116
|
-
@
|
204
|
+
def from(value)
|
205
|
+
@expected_before = value
|
206
|
+
@description_suffix = " from #{description_of value}"
|
207
|
+
self
|
117
208
|
end
|
118
209
|
|
119
|
-
def
|
120
|
-
|
210
|
+
def does_not_match?(event_proc)
|
211
|
+
raise NotImplementedError, "`expect { }.not_to change { }.to()` is not supported"
|
121
212
|
end
|
122
213
|
|
123
|
-
|
124
|
-
|
214
|
+
private
|
215
|
+
|
216
|
+
def change_description
|
217
|
+
"to #{description_of @expected_after}#{@description_suffix}"
|
125
218
|
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# Encapsulates the details of the before/after values.
|
222
|
+
# @api private
|
223
|
+
class ChangeDetails
|
224
|
+
attr_reader :message, :actual_before, :actual_after
|
126
225
|
|
127
|
-
def
|
128
|
-
@
|
226
|
+
def initialize(receiver=nil, message=nil, &block)
|
227
|
+
@message = message ? "##{message}" : "result"
|
228
|
+
@value_proc = block || lambda { receiver.__send__(message) }
|
129
229
|
end
|
130
230
|
|
131
|
-
def
|
132
|
-
@
|
231
|
+
def perform_change(event_proc)
|
232
|
+
@actual_before = evaluate_value_proc
|
233
|
+
event_proc.call
|
234
|
+
@actual_after = evaluate_value_proc
|
133
235
|
end
|
134
236
|
|
135
|
-
def
|
136
|
-
|
237
|
+
def changed?
|
238
|
+
@actual_before != @actual_after
|
239
|
+
end
|
240
|
+
|
241
|
+
def actual_delta
|
242
|
+
@actual_after - @actual_before
|
243
|
+
end
|
244
|
+
|
245
|
+
private
|
246
|
+
|
247
|
+
def evaluate_value_proc
|
248
|
+
case val = @value_proc.call
|
249
|
+
when Enumerable, String
|
250
|
+
val.dup
|
251
|
+
else
|
252
|
+
val
|
253
|
+
end
|
137
254
|
end
|
138
255
|
end
|
139
256
|
end
|