rspec-expectations 3.0.0.beta1 → 3.0.0.beta2
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.
- 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
|