rspec-mocks 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 +1 -1
- data/Changelog.md +95 -3
- data/README.md +27 -13
- data/features/README.md +15 -7
- data/features/argument_matchers/README.md +5 -5
- data/features/argument_matchers/explicit.feature +6 -6
- data/features/argument_matchers/general_matchers.feature +4 -4
- data/features/argument_matchers/type_matchers.feature +2 -2
- data/features/message_expectations/README.md +29 -27
- data/features/message_expectations/call_original.feature +0 -1
- data/features/message_expectations/message_chains_using_expect.feature +49 -0
- data/features/method_stubs/README.md +2 -1
- data/features/method_stubs/{any_instance.feature → allow_any_instance_of.feature} +12 -12
- data/features/method_stubs/{stub_chain.feature → receive_message_chain.feature} +3 -3
- data/features/method_stubs/to_ary.feature +1 -1
- data/features/mutating_constants/stub_defined_constant.feature +0 -1
- data/features/outside_rspec/standalone.feature +1 -1
- data/features/spies/spy_pure_mock_method.feature +1 -1
- data/features/test_frameworks/test_unit.feature +21 -10
- data/features/verifying_doubles/README.md +17 -0
- data/features/verifying_doubles/class_doubles.feature +1 -16
- data/features/verifying_doubles/dynamic_classes.feature +0 -1
- data/features/verifying_doubles/{introduction.feature → instance_doubles.feature} +41 -23
- data/features/verifying_doubles/partial_doubles.feature +2 -2
- data/lib/rspec/mocks.rb +69 -82
- data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +35 -0
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +1 -2
- data/lib/rspec/mocks/any_instance/recorder.rb +52 -18
- data/lib/rspec/mocks/any_instance/stub_chain.rb +1 -1
- data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +4 -0
- data/lib/rspec/mocks/argument_list_matcher.rb +10 -44
- data/lib/rspec/mocks/argument_matchers.rb +132 -163
- data/lib/rspec/mocks/configuration.rb +28 -4
- data/lib/rspec/mocks/error_generator.rb +46 -13
- data/lib/rspec/mocks/example_methods.rb +13 -12
- data/lib/rspec/mocks/extensions/marshal.rb +1 -1
- data/lib/rspec/mocks/framework.rb +3 -4
- data/lib/rspec/mocks/instance_method_stasher.rb +2 -3
- data/lib/rspec/mocks/matchers/have_received.rb +8 -6
- data/lib/rspec/mocks/matchers/receive.rb +28 -20
- data/lib/rspec/mocks/matchers/receive_message_chain.rb +65 -0
- data/lib/rspec/mocks/matchers/receive_messages.rb +3 -2
- data/lib/rspec/mocks/message_chain.rb +91 -0
- data/lib/rspec/mocks/message_expectation.rb +86 -80
- data/lib/rspec/mocks/method_double.rb +2 -11
- data/lib/rspec/mocks/method_reference.rb +82 -23
- data/lib/rspec/mocks/method_signature_verifier.rb +207 -0
- data/lib/rspec/mocks/mutate_const.rb +34 -50
- data/lib/rspec/mocks/object_reference.rb +0 -1
- data/lib/rspec/mocks/proxy.rb +70 -13
- data/lib/rspec/mocks/ruby_features.rb +24 -0
- data/lib/rspec/mocks/space.rb +105 -31
- data/lib/rspec/mocks/standalone.rb +2 -2
- data/lib/rspec/mocks/syntax.rb +43 -8
- data/lib/rspec/mocks/targets.rb +16 -7
- data/lib/rspec/mocks/test_double.rb +41 -15
- data/lib/rspec/mocks/verifying_double.rb +51 -4
- data/lib/rspec/mocks/verifying_message_expecation.rb +12 -12
- data/lib/rspec/mocks/verifying_proxy.rb +32 -19
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/and_call_original_spec.rb +28 -7
- data/spec/rspec/mocks/and_return_spec.rb +23 -0
- data/spec/rspec/mocks/and_yield_spec.rb +1 -2
- data/spec/rspec/mocks/any_instance_spec.rb +33 -17
- data/spec/rspec/mocks/array_including_matcher_spec.rb +6 -6
- data/spec/rspec/mocks/before_all_spec.rb +132 -0
- data/spec/rspec/mocks/block_return_value_spec.rb +12 -1
- data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +9 -11
- data/spec/rspec/mocks/configuration_spec.rb +14 -1
- data/spec/rspec/mocks/double_spec.rb +867 -24
- data/spec/rspec/mocks/example_methods_spec.rb +13 -0
- data/spec/rspec/mocks/extensions/marshal_spec.rb +17 -17
- data/spec/rspec/mocks/failing_argument_matchers_spec.rb +29 -1
- data/spec/rspec/mocks/hash_excluding_matcher_spec.rb +12 -12
- data/spec/rspec/mocks/hash_including_matcher_spec.rb +21 -17
- data/spec/rspec/mocks/instance_method_stasher_spec.rb +2 -3
- data/spec/rspec/mocks/matchers/have_received_spec.rb +7 -0
- data/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +198 -0
- data/spec/rspec/mocks/matchers/receive_messages_spec.rb +2 -2
- data/spec/rspec/mocks/matchers/receive_spec.rb +19 -6
- data/spec/rspec/mocks/method_signature_verifier_spec.rb +272 -0
- data/spec/rspec/mocks/methods_spec.rb +0 -1
- data/spec/rspec/mocks/multiple_return_value_spec.rb +2 -2
- data/spec/rspec/mocks/mutate_const_spec.rb +24 -1
- data/spec/rspec/mocks/nil_expectation_warning_spec.rb +6 -22
- data/spec/rspec/mocks/null_object_mock_spec.rb +13 -7
- data/spec/rspec/mocks/options_hash_spec.rb +3 -3
- data/spec/rspec/mocks/{partial_mock_spec.rb → partial_double_spec.rb} +5 -2
- data/spec/rspec/mocks/{partial_mock_using_mocks_directly_spec.rb → partial_double_using_mocks_directly_spec.rb} +1 -1
- data/spec/rspec/mocks/passing_argument_matchers_spec.rb +18 -0
- data/spec/rspec/mocks/serialization_spec.rb +1 -0
- data/spec/rspec/mocks/space_spec.rb +218 -7
- data/spec/rspec/mocks/stub_chain_spec.rb +6 -0
- data/spec/rspec/mocks/stub_spec.rb +0 -6
- data/spec/rspec/mocks/syntax_spec.rb +19 -0
- data/spec/rspec/mocks/test_double_spec.rb +0 -1
- data/spec/rspec/mocks/verifying_double_spec.rb +281 -18
- data/spec/rspec/mocks/verifying_message_expecation_spec.rb +7 -6
- data/spec/rspec/mocks_spec.rb +168 -42
- data/spec/spec_helper.rb +34 -22
- metadata +94 -63
- metadata.gz.sig +0 -0
- checksums.yaml +0 -15
- checksums.yaml.gz.sig +0 -2
- data/features/outside_rspec/configuration.feature +0 -60
- data/lib/rspec/mocks/arity_calculator.rb +0 -66
- data/lib/rspec/mocks/errors.rb +0 -12
- data/lib/rspec/mocks/mock.rb +0 -7
- data/lib/rspec/mocks/proxy_for_nil.rb +0 -37
- data/lib/rspec/mocks/stub_chain.rb +0 -51
- data/spec/rspec/mocks/argument_expectation_spec.rb +0 -32
- data/spec/rspec/mocks/arity_calculator_spec.rb +0 -95
- data/spec/rspec/mocks/mock_space_spec.rb +0 -113
- data/spec/rspec/mocks/mock_spec.rb +0 -788
@@ -0,0 +1,35 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Mocks
|
3
|
+
module AnyInstance
|
4
|
+
# @private
|
5
|
+
class ExpectChainChain < StubChain
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
@expectation_fulfilled = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def expectation_fulfilled?
|
12
|
+
@expectation_fulfilled
|
13
|
+
end
|
14
|
+
|
15
|
+
def playback!(instance)
|
16
|
+
super.tap { @expectation_fulfilled = true }
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def create_message_expectation_on(instance)
|
22
|
+
::RSpec::Mocks::ExpectChain.expect_chain_on(instance, *@expectation_args, &@expectation_block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def invocation_order
|
26
|
+
@invocation_order ||= {
|
27
|
+
:and_return => [nil],
|
28
|
+
:and_raise => [nil],
|
29
|
+
:and_yield => [nil]
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -23,7 +23,7 @@ module RSpec
|
|
23
23
|
private
|
24
24
|
|
25
25
|
def create_message_expectation_on(instance)
|
26
|
-
proxy = ::RSpec::Mocks.proxy_for(instance)
|
26
|
+
proxy = ::RSpec::Mocks.space.proxy_for(instance)
|
27
27
|
expected_from = IGNORED_BACKTRACE_LINE
|
28
28
|
me = proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block)
|
29
29
|
if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks?
|
@@ -44,4 +44,3 @@ module RSpec
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
47
|
-
|
@@ -49,6 +49,15 @@ module RSpec
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
+
# @api private
|
53
|
+
def expect_chain(*method_names_and_optional_return_values, &block)
|
54
|
+
@expectation_set = true
|
55
|
+
normalize_chain(*method_names_and_optional_return_values) do |method_name, args|
|
56
|
+
observe!(method_name)
|
57
|
+
message_chains.add(method_name, ExpectChainChain.new(self, *args, &block))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
52
61
|
# Initializes the recording a message expectation to be played back
|
53
62
|
# against any instance of this object that invokes the submitted
|
54
63
|
# method.
|
@@ -73,7 +82,7 @@ module RSpec
|
|
73
82
|
raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
|
74
83
|
end
|
75
84
|
message_chains.remove_stub_chains_for!(method_name)
|
76
|
-
::RSpec::Mocks.proxies_of(@klass).each do |proxy|
|
85
|
+
::RSpec::Mocks.space.proxies_of(@klass).each do |proxy|
|
77
86
|
stubs[method_name].each { |stub| proxy.remove_single_stub(method_name, stub) }
|
78
87
|
end
|
79
88
|
stubs[method_name].clear
|
@@ -116,10 +125,38 @@ module RSpec
|
|
116
125
|
end
|
117
126
|
|
118
127
|
def already_observing?(method_name)
|
119
|
-
@observed_methods.include?(method_name)
|
128
|
+
@observed_methods.include?(method_name) || super_class_observing?(method_name)
|
129
|
+
end
|
130
|
+
|
131
|
+
protected
|
132
|
+
|
133
|
+
def stop_observing!(method_name)
|
134
|
+
restore_method!(method_name)
|
135
|
+
@observed_methods.delete(method_name)
|
136
|
+
super_class_observers_for(method_name).each do |ancestor|
|
137
|
+
::RSpec::Mocks.space.
|
138
|
+
any_instance_recorder_for(ancestor).stop_observing!(method_name)
|
139
|
+
end
|
120
140
|
end
|
121
141
|
|
122
|
-
|
142
|
+
private
|
143
|
+
|
144
|
+
def ancestor_is_an_observer?(method_name)
|
145
|
+
lambda do |ancestor|
|
146
|
+
unless ancestor == @klass
|
147
|
+
::RSpec::Mocks.space.
|
148
|
+
any_instance_recorder_for(ancestor).already_observing?(method_name)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def super_class_observers_for(method_name)
|
154
|
+
@klass.ancestors.select(&ancestor_is_an_observer?(method_name))
|
155
|
+
end
|
156
|
+
|
157
|
+
def super_class_observing?(method_name)
|
158
|
+
@klass.ancestors.any?(&ancestor_is_an_observer?(method_name))
|
159
|
+
end
|
123
160
|
|
124
161
|
def normalize_chain(*args)
|
125
162
|
args.shift.to_s.split('.').map {|s| s.to_sym}.reverse.each {|a| args.unshift a}
|
@@ -141,11 +178,13 @@ module RSpec
|
|
141
178
|
end
|
142
179
|
|
143
180
|
def restore_original_method!(method_name)
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
181
|
+
if @klass.instance_method(method_name).owner == @klass
|
182
|
+
alias_method_name = build_alias_method_name(method_name)
|
183
|
+
@klass.class_exec do
|
184
|
+
remove_method method_name
|
185
|
+
alias_method method_name, alias_method_name
|
186
|
+
remove_method alias_method_name
|
187
|
+
end
|
149
188
|
end
|
150
189
|
end
|
151
190
|
|
@@ -163,12 +202,7 @@ module RSpec
|
|
163
202
|
end
|
164
203
|
|
165
204
|
def public_protected_or_private_method_defined?(method_name)
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
def stop_observing!(method_name)
|
170
|
-
restore_method!(method_name)
|
171
|
-
@observed_methods.delete(method_name)
|
205
|
+
MethodReference.method_defined_at_any_visibility?(@klass, method_name)
|
172
206
|
end
|
173
207
|
|
174
208
|
def observe!(method_name)
|
@@ -180,8 +214,8 @@ module RSpec
|
|
180
214
|
@observed_methods << method_name
|
181
215
|
backup_method!(method_name)
|
182
216
|
@klass.__send__(:define_method, method_name) do |*args, &blk|
|
183
|
-
klass = ::RSpec::
|
184
|
-
::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, method_name)
|
217
|
+
klass = ::RSpec::Support.method_handle_for(self, method_name).owner
|
218
|
+
::RSpec::Mocks.space.any_instance_recorder_for(klass).playback!(self, method_name)
|
185
219
|
self.__send__(method_name, *args, &blk)
|
186
220
|
end
|
187
221
|
end
|
@@ -189,8 +223,8 @@ module RSpec
|
|
189
223
|
def mark_invoked!(method_name)
|
190
224
|
backup_method!(method_name)
|
191
225
|
@klass.__send__(:define_method, method_name) do |*args, &blk|
|
192
|
-
klass = ::RSpec::
|
193
|
-
invoked_instance = ::RSpec::Mocks.any_instance_recorder_for(klass).instance_that_received(method_name)
|
226
|
+
klass = ::RSpec::Support.method_handle_for(self, method_name).owner
|
227
|
+
invoked_instance = ::RSpec::Mocks.space.any_instance_recorder_for(klass).instance_that_received(method_name)
|
194
228
|
raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{self.inspect} but has already been received by #{invoked_instance}"
|
195
229
|
end
|
196
230
|
end
|
@@ -12,7 +12,7 @@ module RSpec
|
|
12
12
|
private
|
13
13
|
|
14
14
|
def create_message_expectation_on(instance)
|
15
|
-
proxy = ::RSpec::Mocks.proxy_for(instance)
|
15
|
+
proxy = ::RSpec::Mocks.space.proxy_for(instance)
|
16
16
|
expected_from = IGNORED_BACKTRACE_LINE
|
17
17
|
stub = proxy.add_stub(expected_from, *@expectation_args, &@expectation_block)
|
18
18
|
@recorder.stubs[stub.message] << stub
|
@@ -1,17 +1,18 @@
|
|
1
1
|
require 'rspec/mocks/argument_matchers'
|
2
|
+
require 'rspec/support/fuzzy_matcher'
|
2
3
|
|
3
4
|
module RSpec
|
4
5
|
module Mocks
|
5
6
|
# Wrapper for matching arguments against a list of expected values. Used by
|
6
7
|
# the `with` method on a `MessageExpectation`:
|
7
8
|
#
|
8
|
-
# object.
|
9
|
+
# expect(object).to receive(:message).with(:a, 'b', 3)
|
9
10
|
# object.message(:a, 'b', 3)
|
10
11
|
#
|
11
12
|
# Values passed to `with` can be literal values or argument matchers that
|
12
13
|
# match against the real objects .e.g.
|
13
14
|
#
|
14
|
-
# object.
|
15
|
+
# expect(object).to receive(:message).with(hash_including(:a => 'b'))
|
15
16
|
#
|
16
17
|
# Can also be used directly to match the contents of any `Array`. This
|
17
18
|
# enables 3rd party mocking libs to take advantage of rspec's argument
|
@@ -35,24 +36,17 @@ module RSpec
|
|
35
36
|
# @param [Block] block a block with arity matching the expected
|
36
37
|
#
|
37
38
|
# Initializes an `ArgumentListMatcher` with a collection of literal
|
38
|
-
# values and/or argument matchers
|
39
|
-
# for you.
|
39
|
+
# values and/or argument matchers.
|
40
40
|
#
|
41
41
|
# @see ArgumentMatchers
|
42
42
|
# @see #args_match?
|
43
|
-
def initialize(*expected_args
|
43
|
+
def initialize(*expected_args)
|
44
44
|
@expected_args = expected_args
|
45
|
-
@block = expected_args.empty? ? block : nil
|
46
|
-
@match_any_args = false
|
47
|
-
@matchers = nil
|
48
45
|
|
49
|
-
case expected_args.first
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
@matchers = []
|
54
|
-
else
|
55
|
-
@matchers = expected_args.collect {|arg| matcher_for(arg)}
|
46
|
+
@matchers = case expected_args.first
|
47
|
+
when ArgumentMatchers::AnyArgsMatcher then Array
|
48
|
+
when ArgumentMatchers::NoArgsMatcher then []
|
49
|
+
else expected_args
|
56
50
|
end
|
57
51
|
end
|
58
52
|
|
@@ -64,35 +58,7 @@ module RSpec
|
|
64
58
|
#
|
65
59
|
# @see #initialize
|
66
60
|
def args_match?(*args)
|
67
|
-
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def matcher_for(arg)
|
73
|
-
return ArgumentMatchers::MatcherMatcher.new(arg) if is_matcher?(arg)
|
74
|
-
return ArgumentMatchers::RegexpMatcher.new(arg) if Regexp === arg
|
75
|
-
return ArgumentMatchers::EqualityProxy.new(arg)
|
76
|
-
end
|
77
|
-
|
78
|
-
def is_matcher?(object)
|
79
|
-
return false if object.respond_to?(:i_respond_to_everything_so_im_not_really_a_matcher)
|
80
|
-
|
81
|
-
[:failure_message_for_should, :failure_message].any? do |msg|
|
82
|
-
object.respond_to?(msg)
|
83
|
-
end && object.respond_to?(:matches?)
|
84
|
-
end
|
85
|
-
|
86
|
-
def block_passes?(*args)
|
87
|
-
@block.call(*args) if @block
|
88
|
-
end
|
89
|
-
|
90
|
-
def matchers_match?(*args)
|
91
|
-
@matchers == args
|
92
|
-
end
|
93
|
-
|
94
|
-
def match_any_args?
|
95
|
-
@match_any_args
|
61
|
+
Support::FuzzyMatcher.values_match?(@matchers, args)
|
96
62
|
end
|
97
63
|
|
98
64
|
# Value that will match all argument lists.
|
@@ -10,149 +10,12 @@ module RSpec
|
|
10
10
|
#
|
11
11
|
# @see ArgumentListMatcher
|
12
12
|
module ArgumentMatchers
|
13
|
-
|
14
|
-
class AnyArgsMatcher
|
15
|
-
def description
|
16
|
-
"any args"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class AnyArgMatcher
|
21
|
-
def initialize(ignore)
|
22
|
-
end
|
23
|
-
|
24
|
-
def ==(other)
|
25
|
-
true
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class NoArgsMatcher
|
30
|
-
def description
|
31
|
-
"no args"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
class RegexpMatcher
|
36
|
-
def initialize(regexp)
|
37
|
-
@regexp = regexp
|
38
|
-
end
|
39
|
-
|
40
|
-
def ==(value)
|
41
|
-
Regexp === value ? value == @regexp : value =~ @regexp
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class BooleanMatcher
|
46
|
-
def initialize(ignore)
|
47
|
-
end
|
48
|
-
|
49
|
-
def ==(value)
|
50
|
-
[true,false].include?(value)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
class HashIncludingMatcher
|
55
|
-
def initialize(expected)
|
56
|
-
@expected = expected
|
57
|
-
end
|
58
|
-
|
59
|
-
def ==(actual)
|
60
|
-
@expected.all? {|k,v| actual.has_key?(k) && v == actual[k]}
|
61
|
-
rescue NoMethodError
|
62
|
-
false
|
63
|
-
end
|
64
|
-
|
65
|
-
def description
|
66
|
-
"hash_including(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
class HashExcludingMatcher
|
71
|
-
def initialize(expected)
|
72
|
-
@expected = expected
|
73
|
-
end
|
74
|
-
|
75
|
-
def ==(actual)
|
76
|
-
@expected.none? {|k,v| actual.has_key?(k) && v == actual[k]}
|
77
|
-
rescue NoMethodError
|
78
|
-
false
|
79
|
-
end
|
80
|
-
|
81
|
-
def description
|
82
|
-
"hash_not_including(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
class ArrayIncludingMatcher
|
87
|
-
def initialize(expected)
|
88
|
-
@expected = expected
|
89
|
-
end
|
90
|
-
|
91
|
-
def ==(actual)
|
92
|
-
Set.new(actual).superset?(Set.new(@expected))
|
93
|
-
end
|
94
|
-
|
95
|
-
def description
|
96
|
-
"array_including(#{@expected.join(",")})"
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
class DuckTypeMatcher
|
101
|
-
def initialize(*methods_to_respond_to)
|
102
|
-
@methods_to_respond_to = methods_to_respond_to
|
103
|
-
end
|
104
|
-
|
105
|
-
def ==(value)
|
106
|
-
@methods_to_respond_to.all? {|message| value.respond_to?(message)}
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
class MatcherMatcher
|
111
|
-
def initialize(matcher)
|
112
|
-
@matcher = matcher
|
113
|
-
end
|
114
|
-
|
115
|
-
def ==(value)
|
116
|
-
@matcher.matches?(value)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
class EqualityProxy
|
121
|
-
def initialize(given)
|
122
|
-
@given = given
|
123
|
-
end
|
124
|
-
|
125
|
-
def ==(expected)
|
126
|
-
@given == expected
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
class InstanceOf
|
131
|
-
def initialize(klass)
|
132
|
-
@klass = klass
|
133
|
-
end
|
134
|
-
|
135
|
-
def ==(actual)
|
136
|
-
actual.instance_of?(@klass)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
class KindOf
|
141
|
-
def initialize(klass)
|
142
|
-
@klass = klass
|
143
|
-
end
|
144
|
-
|
145
|
-
def ==(actual)
|
146
|
-
actual.kind_of?(@klass)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
13
|
# Matches any args at all. Supports a more explicit variation of
|
151
|
-
# `object.
|
14
|
+
# `expect(object).to receive(:message)`
|
152
15
|
#
|
153
16
|
# @example
|
154
17
|
#
|
155
|
-
# object.
|
18
|
+
# expect(object).to receive(:message).with(any_args)
|
156
19
|
def any_args
|
157
20
|
AnyArgsMatcher.new
|
158
21
|
end
|
@@ -161,16 +24,16 @@ module RSpec
|
|
161
24
|
#
|
162
25
|
# @example
|
163
26
|
#
|
164
|
-
# object.
|
27
|
+
# expect(object).to receive(:message).with(anything)
|
165
28
|
def anything
|
166
|
-
AnyArgMatcher.new
|
29
|
+
AnyArgMatcher.new
|
167
30
|
end
|
168
31
|
|
169
32
|
# Matches no arguments.
|
170
33
|
#
|
171
34
|
# @example
|
172
35
|
#
|
173
|
-
# object.
|
36
|
+
# expect(object).to receive(:message).with(no_args)
|
174
37
|
def no_args
|
175
38
|
NoArgsMatcher.new
|
176
39
|
end
|
@@ -179,8 +42,8 @@ module RSpec
|
|
179
42
|
#
|
180
43
|
# @example
|
181
44
|
#
|
182
|
-
# object.
|
183
|
-
# object.
|
45
|
+
# expect(object).to receive(:message).with(duck_type(:hello))
|
46
|
+
# expect(object).to receive(:message).with(duck_type(:hello, :goodbye))
|
184
47
|
def duck_type(*args)
|
185
48
|
DuckTypeMatcher.new(*args)
|
186
49
|
end
|
@@ -189,9 +52,9 @@ module RSpec
|
|
189
52
|
#
|
190
53
|
# @example
|
191
54
|
#
|
192
|
-
# object.
|
55
|
+
# expect(object).to receive(:message).with(boolean())
|
193
56
|
def boolean
|
194
|
-
BooleanMatcher.new
|
57
|
+
BooleanMatcher.new
|
195
58
|
end
|
196
59
|
|
197
60
|
# Matches a hash that includes the specified key(s) or key/value pairs.
|
@@ -199,11 +62,11 @@ module RSpec
|
|
199
62
|
#
|
200
63
|
# @example
|
201
64
|
#
|
202
|
-
# object.
|
203
|
-
# object.
|
204
|
-
# object.
|
65
|
+
# expect(object).to receive(:message).with(hash_including(:key => val))
|
66
|
+
# expect(object).to receive(:message).with(hash_including(:key))
|
67
|
+
# expect(object).to receive(:message).with(hash_including(:key, :key2 => val2))
|
205
68
|
def hash_including(*args)
|
206
|
-
HashIncludingMatcher.new(anythingize_lonely_keys(*args))
|
69
|
+
HashIncludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args))
|
207
70
|
end
|
208
71
|
|
209
72
|
# Matches an array that includes the specified items at least once.
|
@@ -211,8 +74,8 @@ module RSpec
|
|
211
74
|
#
|
212
75
|
# @example
|
213
76
|
#
|
214
|
-
# object.
|
215
|
-
# object.
|
77
|
+
# expect(object).to receive(:message).with(array_including(1,2,3))
|
78
|
+
# expect(object).to receive(:message).with(array_including([1,2,3]))
|
216
79
|
def array_including(*args)
|
217
80
|
actually_an_array = Array === args.first && args.count == 1 ? args.first : args
|
218
81
|
ArrayIncludingMatcher.new(actually_an_array)
|
@@ -222,11 +85,11 @@ module RSpec
|
|
222
85
|
#
|
223
86
|
# @example
|
224
87
|
#
|
225
|
-
# object.
|
226
|
-
# object.
|
227
|
-
# object.
|
88
|
+
# expect(object).to receive(:message).with(hash_excluding(:key => val))
|
89
|
+
# expect(object).to receive(:message).with(hash_excluding(:key))
|
90
|
+
# expect(object).to receive(:message).with(hash_excluding(:key, :key2 => :val2))
|
228
91
|
def hash_excluding(*args)
|
229
|
-
HashExcludingMatcher.new(anythingize_lonely_keys(*args))
|
92
|
+
HashExcludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args))
|
230
93
|
end
|
231
94
|
|
232
95
|
alias_method :hash_not_including, :hash_excluding
|
@@ -235,7 +98,7 @@ module RSpec
|
|
235
98
|
#
|
236
99
|
# @example
|
237
100
|
#
|
238
|
-
# object.
|
101
|
+
# expect(object).to receive(:message).with(instance_of(Thing))
|
239
102
|
def instance_of(klass)
|
240
103
|
InstanceOf.new(klass)
|
241
104
|
end
|
@@ -245,20 +108,126 @@ module RSpec
|
|
245
108
|
# Matches if `arg.kind_of?(klass)`
|
246
109
|
# @example
|
247
110
|
#
|
248
|
-
# object.
|
111
|
+
# expect(object).to receive(:message).with(kind_of(Thing))
|
249
112
|
def kind_of(klass)
|
250
|
-
|
113
|
+
klass
|
251
114
|
end
|
252
115
|
|
253
116
|
alias_method :a_kind_of, :kind_of
|
254
117
|
|
255
|
-
private
|
256
|
-
|
257
|
-
def anythingize_lonely_keys(*args)
|
118
|
+
# @api private
|
119
|
+
def self.anythingize_lonely_keys(*args)
|
258
120
|
hash = args.last.class == Hash ? args.delete_at(-1) : {}
|
259
|
-
args.each { | arg | hash[arg] =
|
121
|
+
args.each { | arg | hash[arg] = AnyArgMatcher.new }
|
260
122
|
hash
|
261
123
|
end
|
124
|
+
|
125
|
+
# @api private
|
126
|
+
class AnyArgsMatcher
|
127
|
+
def description
|
128
|
+
"any args"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# @api private
|
133
|
+
class AnyArgMatcher
|
134
|
+
def ===(other)
|
135
|
+
true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# @api private
|
140
|
+
class NoArgsMatcher
|
141
|
+
def description
|
142
|
+
"no args"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# @api private
|
147
|
+
class BooleanMatcher
|
148
|
+
def ===(value)
|
149
|
+
true == value || false == value
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# @api private
|
154
|
+
class BaseHashMatcher
|
155
|
+
def initialize(expected)
|
156
|
+
@expected = expected
|
157
|
+
end
|
158
|
+
|
159
|
+
def ===(predicate, actual)
|
160
|
+
@expected.__send__(predicate) do |k, v|
|
161
|
+
actual.has_key?(k) && Support::FuzzyMatcher.values_match?(v, actual[k])
|
162
|
+
end
|
163
|
+
rescue NoMethodError
|
164
|
+
false
|
165
|
+
end
|
166
|
+
|
167
|
+
def description(name)
|
168
|
+
"#{name}(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# @api private
|
173
|
+
class HashIncludingMatcher < BaseHashMatcher
|
174
|
+
def ===(actual)
|
175
|
+
super(:all?, actual)
|
176
|
+
end
|
177
|
+
|
178
|
+
def description
|
179
|
+
super("hash_including")
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# @api private
|
184
|
+
class HashExcludingMatcher < BaseHashMatcher
|
185
|
+
def ===(actual)
|
186
|
+
super(:none?, actual)
|
187
|
+
end
|
188
|
+
|
189
|
+
def description
|
190
|
+
super("hash_not_including")
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# @api private
|
195
|
+
class ArrayIncludingMatcher
|
196
|
+
def initialize(expected)
|
197
|
+
@expected = expected
|
198
|
+
end
|
199
|
+
|
200
|
+
def ===(actual)
|
201
|
+
Set.new(actual).superset?(Set.new(@expected))
|
202
|
+
end
|
203
|
+
|
204
|
+
def description
|
205
|
+
"array_including(#{@expected.join(",")})"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# @api private
|
210
|
+
class DuckTypeMatcher
|
211
|
+
def initialize(*methods_to_respond_to)
|
212
|
+
@methods_to_respond_to = methods_to_respond_to
|
213
|
+
end
|
214
|
+
|
215
|
+
def ===(value)
|
216
|
+
@methods_to_respond_to.all? {|message| value.respond_to?(message)}
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# @api private
|
221
|
+
class InstanceOf
|
222
|
+
def initialize(klass)
|
223
|
+
@klass = klass
|
224
|
+
end
|
225
|
+
|
226
|
+
def ===(actual)
|
227
|
+
actual.instance_of?(@klass)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
262
231
|
end
|
263
232
|
end
|
264
233
|
end
|