rr 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +10 -0
- data/README.rdoc +56 -18
- data/Rakefile +1 -2
- data/lib/rr.rb +14 -5
- data/lib/rr/adapters/rr_methods.rb +11 -0
- data/lib/rr/adapters/rspec.rb +30 -0
- data/lib/rr/adapters/test_unit.rb +4 -0
- data/lib/rr/double.rb +79 -227
- data/lib/rr/double_definitions/child_double_definition_creator.rb +4 -0
- data/lib/rr/double_definitions/double_definition.rb +138 -4
- data/lib/rr/double_definitions/double_definition_creator.rb +18 -4
- data/lib/rr/double_definitions/double_definition_creator_proxy.rb +35 -3
- data/lib/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation.rb +17 -0
- data/lib/rr/double_definitions/strategies/scope/instance_of_class.rb +3 -0
- data/lib/rr/double_injection.rb +10 -6
- data/lib/rr/errors/spy_verification_errors/double_injection_not_found_error.rb +8 -0
- data/lib/rr/errors/spy_verification_errors/invocation_count_error.rb +8 -0
- data/lib/rr/errors/spy_verification_errors/spy_verification_error.rb +8 -0
- data/lib/rr/errors/subject_does_not_implement_method_error.rb +6 -0
- data/lib/rr/errors/subject_has_different_arity_error.rb +6 -0
- data/lib/rr/expectations/times_called_expectation.rb +11 -9
- data/lib/rr/recorded_calls.rb +103 -0
- data/lib/rr/space.rb +18 -8
- data/lib/rr/spy_verification.rb +48 -0
- data/lib/rr/spy_verification_proxy.rb +18 -0
- data/lib/rr/times_called_matchers/any_times_matcher.rb +2 -3
- data/lib/rr/times_called_matchers/at_least_matcher.rb +2 -3
- data/lib/rr/times_called_matchers/at_most_matcher.rb +2 -3
- data/lib/rr/times_called_matchers/times_called_matcher.rb +4 -4
- data/spec/core_spec_suite.rb +1 -0
- data/spec/high_level_spec.rb +151 -14
- data/spec/rr/adapters/rr_methods_space_spec.rb +2 -2
- data/spec/rr/double_definitions/child_double_definition_creator_spec.rb +9 -0
- data/spec/rr/double_definitions/double_definition_creator_proxy_spec.rb +91 -19
- data/spec/rr/double_definitions/double_definition_creator_spec.rb +7 -0
- data/spec/rr/double_definitions/double_definition_spec.rb +53 -10
- data/spec/rr/double_injection/double_injection_dispatching_spec.rb +14 -15
- data/spec/rr/double_injection/double_injection_verify_spec.rb +1 -1
- data/spec/rr/double_spec.rb +79 -445
- data/spec/rr/errors/rr_error_spec.rb +49 -47
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_any_times_spec.rb +4 -3
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_least_spec.rb +3 -2
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_most_spec.rb +3 -2
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_helper.rb +2 -2
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_integer_spec.rb +3 -3
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_proc_spec.rb +6 -5
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_range_spec.rb +3 -2
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_spec.rb +3 -2
- data/spec/rr/rspec/invocation_matcher_spec.rb +259 -0
- data/spec/rr/rspec/rspec_adapter_spec.rb +1 -1
- data/spec/rr/rspec/rspec_usage_spec.rb +43 -24
- data/spec/rr/space/hash_with_object_id_key_spec.rb +2 -2
- data/spec/rr/space/space_spec.rb +27 -9
- data/spec/rr/test_unit/test_unit_integration_test.rb +10 -0
- data/spec/spec_helper.rb +4 -1
- data/spec/spy_verification_spec.rb +129 -0
- metadata +100 -88
data/CHANGES
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
* 0.7.0
|
2
|
+
- Added spies (Patchs by Joe Ferris, Michael Niessner & Mike Mangino)
|
3
|
+
- Added strongly typed reimplementation doubles (Patch by Michael Niessner)
|
4
|
+
|
5
|
+
* 0.6.2
|
6
|
+
- Fixed DoubleDefinition chaining edge cases
|
7
|
+
|
8
|
+
* 0.6.1
|
9
|
+
- DoubleDefinitionCreatorProxy definition eval block is instance_evaled when the arity is not 1. When the arity is 1, the block is yielded with the DoubleDefinitionCreatorProxy passed in.
|
10
|
+
|
1
11
|
* 0.6.0
|
2
12
|
- Friendlier DoubleNotFound error message
|
3
13
|
- Implemented Double strategy creation methods (#mock, #stub, #proxy, #instance_of, and ! equivalents) on DoubleDefinition
|
data/README.rdoc
CHANGED
@@ -23,8 +23,7 @@ double for tests. The following are test doubles:
|
|
23
23
|
* Proxies
|
24
24
|
http://xunitpatterns.com/Test%20Double.html
|
25
25
|
|
26
|
-
Currently RR implements mocks, stubs, and
|
27
|
-
support spies.
|
26
|
+
Currently RR implements mocks, stubs, proxies, and spies. Fakes usually require custom code, so it is beyond the scope of RR.
|
28
27
|
|
29
28
|
== Using RR
|
30
29
|
=== test/unit
|
@@ -202,14 +201,50 @@ Put double scenarios on instances of a Class.
|
|
202
201
|
|
203
202
|
mock.instance_of(User).valid? {false}
|
204
203
|
|
204
|
+
=== Spies
|
205
|
+
|
206
|
+
Adding a DoubleInjection to an Object + Method (done by stub, mock, or dont_allow) causes RR to record any method
|
207
|
+
invocations to the Object + method. Assertions can then be made on the recorded method calls.
|
208
|
+
|
209
|
+
==== test/unit
|
210
|
+
|
211
|
+
subject = Object.new
|
212
|
+
stub(subject).foo
|
213
|
+
subject.foo(1)
|
214
|
+
assert_received(subject) {|subject| subject.foo(1)}
|
215
|
+
assert_received(subject) {|subject| subject.bar} # This fails
|
216
|
+
|
217
|
+
==== rspec
|
218
|
+
|
219
|
+
subject = Object.new
|
220
|
+
stub(subject).foo
|
221
|
+
subject.foo(1)
|
222
|
+
subject.should have_received.foo(1)
|
223
|
+
subject.should have_received.bar # this fails
|
224
|
+
|
205
225
|
=== Block Syntax
|
226
|
+
The block syntax has two modes
|
227
|
+
* A normal block mode with a DoubleDefinitionCreatorProxy argument
|
228
|
+
|
206
229
|
script = MyScript.new
|
207
|
-
mock(script) do |
|
208
|
-
|
209
|
-
|
210
|
-
|
230
|
+
mock(script) do |expect|
|
231
|
+
expect.system("cd #{RAILS_ENV}") {true}
|
232
|
+
expect.system("rake foo:bar") {true}
|
233
|
+
expect.system("rake baz") {true}
|
211
234
|
end
|
212
235
|
|
236
|
+
* An instance_eval mode where the DoubleDefinitionCreatorProxy is instance_evaled
|
237
|
+
|
238
|
+
script = MyScript.new
|
239
|
+
mock(script) do
|
240
|
+
system("cd #{RAILS_ENV}") {true}
|
241
|
+
system("rake foo:bar") {true}
|
242
|
+
system("rake baz") {true}
|
243
|
+
end
|
244
|
+
|
245
|
+
=== Block Syntax with explicit DoubleDefinitionCreatorProxy argument
|
246
|
+
|
247
|
+
|
213
248
|
=== Double Graphs
|
214
249
|
RR has a method-chaining api support for Double graphs. For example,
|
215
250
|
lets say you want an object to receive a method call to #foo, and have
|
@@ -273,19 +308,22 @@ In RR, you would do:
|
|
273
308
|
== Special Thanks To
|
274
309
|
With any development effort, there are countless people who have contributed
|
275
310
|
to making it possible. We all are standing on the shoulders of giants.
|
276
|
-
*
|
277
|
-
*
|
278
|
-
*
|
279
|
-
*
|
280
|
-
* Parker Thompson for pairing with me
|
311
|
+
* Aslak Hellesoy for Developing Rspec
|
312
|
+
* Dan North for syntax ideas
|
313
|
+
* Dave Astels for some BDD inspiration
|
314
|
+
* David Chelimsky for encouragement to make the RR framework, for developing the Rspec mock framework, and syntax ideas
|
281
315
|
* Felix Morio for pairing with me
|
282
|
-
* Jeff Whitmire for documentation suggestions
|
283
|
-
* David Chelimsky for encouragement to make the RR framework, for developing
|
284
|
-
the Rspec mock framework, and syntax ideas
|
285
316
|
* Gerard Meszaros for his excellent book "xUnit Test Patterns"
|
286
|
-
* Dan North for syntax ideas
|
287
|
-
* Jim Weirich for developing Flexmock, the first Terse ruby mock framework in Ruby
|
288
317
|
* James Mead for developing Mocha
|
289
|
-
*
|
318
|
+
* Joe Ferris for patches
|
319
|
+
* Jeff Whitmire for documentation suggestions
|
320
|
+
* Jim Weirich for developing Flexmock, the first Terse ruby mock framework in Ruby
|
321
|
+
* Joe Ferris for patches
|
322
|
+
* Matthew O'Conner for patches
|
323
|
+
* Michael Niessner for patches and pairing with me
|
324
|
+
* Mike Mangino (from Elevated Rails) for patches and pairing with me
|
325
|
+
* Nick Kallen for documentation suggestions, bug reports, and patches
|
326
|
+
* Nathan Sobo for various ideas and inspiration for cleaner and more expressive code
|
327
|
+
* Parker Thompson for pairing with me
|
328
|
+
* Pivotal Labs for sponsoring RR development
|
290
329
|
* Stephen Baker for Developing Rspec
|
291
|
-
* Dave Astels for some BDD inspiration
|
data/Rakefile
CHANGED
@@ -26,7 +26,7 @@ def run_suite
|
|
26
26
|
end
|
27
27
|
|
28
28
|
PKG_NAME = "rr"
|
29
|
-
PKG_VERSION = "0.
|
29
|
+
PKG_VERSION = "0.7.0"
|
30
30
|
PKG_FILES = FileList[
|
31
31
|
'[A-Z]*',
|
32
32
|
'*.rb',
|
@@ -52,7 +52,6 @@ spec = Gem::Specification.new do |s|
|
|
52
52
|
|
53
53
|
s.test_files = Dir.glob('spec/*_spec.rb')
|
54
54
|
s.require_path = 'lib'
|
55
|
-
s.autorequire = 'rr'
|
56
55
|
s.author = "Brian Takita"
|
57
56
|
s.email = "brian@pivotallabs.com"
|
58
57
|
s.homepage = "http://pivotallabs.com"
|
data/lib/rr.rb
CHANGED
@@ -1,14 +1,22 @@
|
|
1
1
|
dir = File.dirname(__FILE__)
|
2
|
+
require 'rubygems'
|
3
|
+
|
2
4
|
require "#{dir}/rr/errors/rr_error"
|
5
|
+
require "#{dir}/rr/errors/subject_does_not_implement_method_error"
|
6
|
+
require "#{dir}/rr/errors/subject_has_different_arity_error"
|
3
7
|
require "#{dir}/rr/errors/double_definition_error"
|
4
8
|
require "#{dir}/rr/errors/double_not_found_error"
|
5
9
|
require "#{dir}/rr/errors/double_order_error"
|
6
10
|
require "#{dir}/rr/errors/argument_equality_error"
|
7
11
|
require "#{dir}/rr/errors/times_called_error"
|
12
|
+
require "#{dir}/rr/errors/spy_verification_errors/spy_verification_error"
|
13
|
+
require "#{dir}/rr/errors/spy_verification_errors/double_injection_not_found_error"
|
14
|
+
require "#{dir}/rr/errors/spy_verification_errors/invocation_count_error"
|
8
15
|
|
9
16
|
require "#{dir}/rr/space"
|
10
17
|
require "#{dir}/rr/double_injection"
|
11
18
|
require "#{dir}/rr/hash_with_object_id_key"
|
19
|
+
require "#{dir}/rr/recorded_calls"
|
12
20
|
|
13
21
|
require "#{dir}/rr/double_definitions/double_definition_creator_proxy"
|
14
22
|
require "#{dir}/rr/double_definitions/double_definition_creator"
|
@@ -24,6 +32,7 @@ require "#{dir}/rr/double_definitions/strategies/verification/stub"
|
|
24
32
|
require "#{dir}/rr/double_definitions/strategies/verification/dont_allow"
|
25
33
|
require "#{dir}/rr/double_definitions/strategies/implementation/implementation_strategy"
|
26
34
|
require "#{dir}/rr/double_definitions/strategies/implementation/reimplementation"
|
35
|
+
require "#{dir}/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation"
|
27
36
|
require "#{dir}/rr/double_definitions/strategies/implementation/proxy"
|
28
37
|
require "#{dir}/rr/double_definitions/strategies/scope/scope_strategy"
|
29
38
|
require "#{dir}/rr/double_definitions/strategies/scope/instance"
|
@@ -54,11 +63,15 @@ require "#{dir}/rr/times_called_matchers/proc_matcher"
|
|
54
63
|
require "#{dir}/rr/times_called_matchers/at_least_matcher"
|
55
64
|
require "#{dir}/rr/times_called_matchers/at_most_matcher"
|
56
65
|
|
66
|
+
require "#{dir}/rr/spy_verification_proxy"
|
67
|
+
require "#{dir}/rr/spy_verification"
|
68
|
+
|
57
69
|
require "#{dir}/rr/adapters/rspec"
|
58
70
|
require "#{dir}/rr/adapters/test_unit"
|
59
71
|
|
60
72
|
module RR
|
61
73
|
class << self
|
74
|
+
include Adapters::RRMethods
|
62
75
|
(RR::Space.instance_methods - Object.instance_methods).each do |method_name|
|
63
76
|
returns_method = <<-METHOD
|
64
77
|
def #{method_name}(*args, &block)
|
@@ -67,9 +80,5 @@ module RR
|
|
67
80
|
METHOD
|
68
81
|
class_eval(returns_method, __FILE__, __LINE__ - 4)
|
69
82
|
end
|
70
|
-
|
71
|
-
def method_missing(method_name, *args, &block)
|
72
|
-
RR::Space.instance.__send__(method_name, *args, &block)
|
73
|
-
end
|
74
83
|
end
|
75
|
-
end
|
84
|
+
end
|
@@ -99,7 +99,18 @@ module RR
|
|
99
99
|
expectation_proc ||= block
|
100
100
|
RR::WildcardMatchers::Satisfy.new(expectation_proc)
|
101
101
|
end
|
102
|
+
|
103
|
+
def spy(subject)
|
104
|
+
methods_to_stub = subject.public_methods - ["methods", "==", "__send__", "__id__"]
|
105
|
+
methods_to_stub.each do |method|
|
106
|
+
stub.proxy(subject, method)
|
107
|
+
end
|
108
|
+
end
|
102
109
|
|
110
|
+
def received(subject)
|
111
|
+
RR::SpyVerificationProxy.new(subject)
|
112
|
+
end
|
113
|
+
|
103
114
|
instance_methods.each do |name|
|
104
115
|
alias_method "rr_#{name}", name
|
105
116
|
end
|
data/lib/rr/adapters/rspec.rb
CHANGED
@@ -18,6 +18,36 @@ module RR
|
|
18
18
|
def teardown_mocks_for_rspec
|
19
19
|
RR.reset
|
20
20
|
end
|
21
|
+
|
22
|
+
def have_received(method = nil)
|
23
|
+
InvocationMatcher.new(method)
|
24
|
+
end
|
25
|
+
|
26
|
+
class InvocationMatcher < SpyVerificationProxy
|
27
|
+
attr_reader :failure_message
|
28
|
+
|
29
|
+
def initialize(method = nil)
|
30
|
+
method_missing(method) if method
|
31
|
+
end
|
32
|
+
|
33
|
+
def matches?(subject)
|
34
|
+
@verification.subject = subject
|
35
|
+
RR::Space.instance.recorded_calls.match_error(@verification) ? false : true
|
36
|
+
end
|
37
|
+
|
38
|
+
def nil?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
def method_missing(method_name, *args, &block)
|
43
|
+
if @verification
|
44
|
+
@verification.send(method_name, *args)
|
45
|
+
else
|
46
|
+
@verification = super
|
47
|
+
end
|
48
|
+
self
|
49
|
+
end
|
50
|
+
end
|
21
51
|
end
|
22
52
|
end
|
23
53
|
end
|
data/lib/rr/double.rb
CHANGED
@@ -16,7 +16,7 @@ module RR
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
attr_reader :times_called, :double_injection, :definition
|
19
|
+
attr_reader :times_called, :double_injection, :definition, :times_called_expectation
|
20
20
|
include Space::Reader
|
21
21
|
|
22
22
|
def initialize(double_injection, definition)
|
@@ -25,193 +25,10 @@ module RR
|
|
25
25
|
@times_called = 0
|
26
26
|
@times_called_expectation = Expectations::TimesCalledExpectation.new(self)
|
27
27
|
definition.double = self
|
28
|
+
verify_method_signature if definition.verify_method_signature?
|
28
29
|
double_injection.register_double self
|
29
30
|
end
|
30
|
-
|
31
|
-
# Double#with sets the expectation that the Double will receive
|
32
|
-
# the passed in arguments.
|
33
|
-
#
|
34
|
-
# Passing in a block sets the return value.
|
35
|
-
#
|
36
|
-
# mock(subject).method_name.with(1, 2) {:return_value}
|
37
|
-
def with(*args, &returns)
|
38
|
-
definition.with(*args, &returns)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Double#with_any_args sets the expectation that the Double can receive
|
42
|
-
# any arguments.
|
43
|
-
#
|
44
|
-
# Passing in a block sets the return value.
|
45
|
-
#
|
46
|
-
# mock(subject).method_name.with_any_args {:return_value}
|
47
|
-
def with_any_args(&returns)
|
48
|
-
definition.with_any_args(&returns)
|
49
|
-
end
|
50
|
-
|
51
|
-
# Double#with_no_args sets the expectation that the Double will receive
|
52
|
-
# no arguments.
|
53
|
-
#
|
54
|
-
# Passing in a block sets the return value.
|
55
|
-
#
|
56
|
-
# mock(subject).method_name.with_no_args {:return_value}
|
57
|
-
def with_no_args(&returns)
|
58
|
-
definition.with_no_args(&returns)
|
59
|
-
end
|
60
|
-
|
61
|
-
# Double#never sets the expectation that the Double will never be
|
62
|
-
# called.
|
63
|
-
#
|
64
|
-
# This method does not accept a block because it will never be called.
|
65
|
-
#
|
66
|
-
# mock(subject).method_name.never
|
67
|
-
def never
|
68
|
-
definition.never
|
69
|
-
end
|
70
|
-
|
71
|
-
# Double#once sets the expectation that the Double will be called
|
72
|
-
# 1 time.
|
73
|
-
#
|
74
|
-
# Passing in a block sets the return value.
|
75
|
-
#
|
76
|
-
# mock(subject).method_name.once {:return_value}
|
77
|
-
def once(&returns)
|
78
|
-
definition.once(&returns)
|
79
|
-
end
|
80
|
-
|
81
|
-
# Double#twice sets the expectation that the Double will be called
|
82
|
-
# 2 times.
|
83
|
-
#
|
84
|
-
# Passing in a block sets the return value.
|
85
|
-
#
|
86
|
-
# mock(subject).method_name.twice {:return_value}
|
87
|
-
def twice(&returns)
|
88
|
-
definition.twice(&returns)
|
89
|
-
end
|
90
|
-
|
91
|
-
# Double#at_least sets the expectation that the Double
|
92
|
-
# will be called at least n times.
|
93
|
-
# It works by creating a TimesCalledExpectation.
|
94
|
-
#
|
95
|
-
# Passing in a block sets the return value.
|
96
|
-
#
|
97
|
-
# mock(subject).method_name.at_least(4) {:return_value}
|
98
|
-
def at_least(number, &returns)
|
99
|
-
definition.at_least(number, &returns)
|
100
|
-
end
|
101
|
-
|
102
|
-
# Double#at_most allows sets the expectation that the Double
|
103
|
-
# will be called at most n times.
|
104
|
-
# It works by creating a TimesCalledExpectation.
|
105
|
-
#
|
106
|
-
# Passing in a block sets the return value.
|
107
|
-
#
|
108
|
-
# mock(subject).method_name.at_most(4) {:return_value}
|
109
|
-
def at_most(number, &returns)
|
110
|
-
definition.at_most(number, &returns)
|
111
|
-
end
|
112
|
-
|
113
|
-
# Double#any_number_of_times sets an that the Double will be called
|
114
|
-
# any number of times. This effectively removes the times called expectation
|
115
|
-
# from the Doublen
|
116
|
-
#
|
117
|
-
# Passing in a block sets the return value.
|
118
|
-
#
|
119
|
-
# mock(subject).method_name.any_number_of_times
|
120
|
-
def any_number_of_times(&returns)
|
121
|
-
definition.any_number_of_times(&returns)
|
122
|
-
end
|
123
|
-
|
124
|
-
# Double#times creates an TimesCalledExpectation of the passed
|
125
|
-
# in number.
|
126
|
-
#
|
127
|
-
# Passing in a block sets the return value.
|
128
|
-
#
|
129
|
-
# mock(subject).method_name.times(4) {:return_value}
|
130
|
-
def times(number, &returns)
|
131
|
-
definition.times(number, &returns)
|
132
|
-
end
|
133
|
-
|
134
|
-
# Double#ordered sets the Double to have an ordered
|
135
|
-
# expectation.
|
136
|
-
#
|
137
|
-
# Passing in a block sets the return value.
|
138
|
-
#
|
139
|
-
# mock(subject).method_name.ordered {return_value}
|
140
|
-
def ordered(&returns)
|
141
|
-
definition.ordered(&returns)
|
142
|
-
end
|
143
|
-
|
144
|
-
# Double#ordered? returns true when the Double is ordered.
|
145
|
-
#
|
146
|
-
# mock(subject).method_name.ordered?
|
147
|
-
def ordered?
|
148
|
-
definition.ordered?
|
149
|
-
end
|
150
|
-
|
151
|
-
# Double#verbose sets the Double to print out each method call it receives.
|
152
|
-
#
|
153
|
-
# Passing in a block sets the return value
|
154
|
-
def verbose(&block)
|
155
|
-
definition.verbose(&block)
|
156
|
-
end
|
157
|
-
|
158
|
-
# Double#verbose? returns true when verbose has been called on it. It returns
|
159
|
-
# true when the double is set to print each method call it receives.
|
160
|
-
def verbose?
|
161
|
-
definition.verbose?
|
162
|
-
end
|
163
|
-
|
164
|
-
# Double#yields sets the Double to invoke a passed in block when
|
165
|
-
# the Double is called.
|
166
|
-
# An Expection will be raised if no block is passed in when the
|
167
|
-
# Double is called.
|
168
|
-
#
|
169
|
-
# Passing in a block sets the return value.
|
170
|
-
#
|
171
|
-
# mock(subject).method_name.yields(yield_arg1, yield_arg2) {return_value}
|
172
|
-
# subject.method_name {|yield_arg1, yield_arg2|}
|
173
|
-
def yields(*args, &returns)
|
174
|
-
definition.yields(*args, &returns)
|
175
|
-
end
|
176
|
-
|
177
|
-
# Double#after_call creates a callback that occurs after call
|
178
|
-
# is called. The passed in block receives the return value of
|
179
|
-
# the Double being called.
|
180
|
-
# An Expection will be raised if no block is passed in.
|
181
|
-
#
|
182
|
-
# mock(subject).method_name {return_value}.after_call {|return_value|}
|
183
|
-
# subject.method_name # return_value
|
184
|
-
#
|
185
|
-
# This feature is built into proxies.
|
186
|
-
# mock.proxy(User).find('1') {|user| mock(user).valid? {false}}
|
187
|
-
def after_call(&block)
|
188
|
-
definition.after_call &block
|
189
|
-
end
|
190
|
-
|
191
|
-
# Double#returns accepts an argument value or a block.
|
192
|
-
# It will raise an ArgumentError if both are passed in.
|
193
|
-
#
|
194
|
-
# Passing in a block causes Double to return the return value of
|
195
|
-
# the passed in block.
|
196
|
-
#
|
197
|
-
# Passing in an argument causes Double to return the argument.
|
198
|
-
def returns(*args, &implementation)
|
199
|
-
definition.returns(*args, &implementation)
|
200
|
-
end
|
201
|
-
|
202
|
-
# Double#implemented_by sets the implementation of the Double.
|
203
|
-
# This method takes a Proc or a Method. Passing in a Method allows
|
204
|
-
# the Double to accept blocks.
|
205
|
-
#
|
206
|
-
# obj = Object.new
|
207
|
-
# def obj.foobar
|
208
|
-
# yield(1)
|
209
|
-
# end
|
210
|
-
# mock(obj).method_name.implemented_by(obj.method(:foobar))
|
211
|
-
def implemented_by(implementation)
|
212
|
-
definition.implemented_by implementation
|
213
|
-
end
|
214
|
-
|
31
|
+
|
215
32
|
# Double#call calls the Double's implementation. The return
|
216
33
|
# value of the implementation is returned.
|
217
34
|
#
|
@@ -228,23 +45,6 @@ module RR
|
|
228
45
|
definition.after_call_proc ? extract_subject_from_return_value(definition.after_call_proc.call(return_value)) : return_value
|
229
46
|
end
|
230
47
|
|
231
|
-
def yields!(block)
|
232
|
-
if definition.yields_value
|
233
|
-
if block
|
234
|
-
block.call(*definition.yields_value)
|
235
|
-
else
|
236
|
-
raise ArgumentError, "A Block must be passed into the method call when using yields"
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
240
|
-
protected :yields!
|
241
|
-
|
242
|
-
def call_implementation(double_injection, *args, &block)
|
243
|
-
return_value = do_call_implementation_and_get_return_value(double_injection, *args, &block)
|
244
|
-
extract_subject_from_return_value(return_value)
|
245
|
-
end
|
246
|
-
protected :call_implementation
|
247
|
-
|
248
48
|
# Double#exact_match? returns true when the passed in arguments
|
249
49
|
# exactly match the ArgumentEqualityExpectation arguments.
|
250
50
|
def exact_match?(*arguments)
|
@@ -260,7 +60,7 @@ module RR
|
|
260
60
|
# Double#attempt? returns true when the
|
261
61
|
# TimesCalledExpectation is satisfied.
|
262
62
|
def attempt?
|
263
|
-
|
63
|
+
verify_times_matcher_is_set
|
264
64
|
times_called_expectation.attempt?
|
265
65
|
end
|
266
66
|
|
@@ -268,13 +68,13 @@ module RR
|
|
268
68
|
# is satisfied for this double. A TimesCalledError
|
269
69
|
# is raised if the TimesCalledExpectation is not met.
|
270
70
|
def verify
|
271
|
-
|
71
|
+
verify_times_matcher_is_set
|
272
72
|
times_called_expectation.verify!
|
273
73
|
true
|
274
74
|
end
|
275
75
|
|
276
76
|
def terminal?
|
277
|
-
|
77
|
+
verify_times_matcher_is_set
|
278
78
|
times_called_expectation.terminal?
|
279
79
|
end
|
280
80
|
|
@@ -285,41 +85,85 @@ module RR
|
|
285
85
|
|
286
86
|
# The Arguments that this Double expects
|
287
87
|
def expected_arguments
|
288
|
-
|
88
|
+
verify_argument_expectation_is_set
|
289
89
|
argument_expectation.expected_arguments
|
290
90
|
end
|
291
91
|
|
292
92
|
# The TimesCalledMatcher for the TimesCalledExpectation
|
293
93
|
def times_matcher
|
294
|
-
|
94
|
+
definition.times_matcher
|
295
95
|
end
|
296
96
|
|
297
|
-
def
|
298
|
-
|
299
|
-
@times_called_expectation
|
97
|
+
def formatted_name
|
98
|
+
self.class.formatted_name(method_name, expected_arguments)
|
300
99
|
end
|
301
100
|
|
302
|
-
|
303
|
-
|
101
|
+
protected
|
102
|
+
def ordered?
|
103
|
+
definition.ordered?
|
104
|
+
end
|
105
|
+
|
106
|
+
def verbose?
|
107
|
+
definition.verbose?
|
304
108
|
end
|
305
|
-
|
306
|
-
|
109
|
+
|
110
|
+
def yields!(block)
|
111
|
+
if definition.yields_value
|
112
|
+
if block
|
113
|
+
block.call(*definition.yields_value)
|
114
|
+
else
|
115
|
+
raise ArgumentError, "A Block must be passed into the method call when using yields"
|
116
|
+
end
|
117
|
+
end
|
307
118
|
end
|
308
|
-
protected :implementation=
|
309
119
|
|
310
|
-
def
|
311
|
-
|
120
|
+
def call_implementation(double_injection, *args, &block)
|
121
|
+
return_value = do_call_implementation_and_get_return_value(double_injection, *args, &block)
|
122
|
+
extract_subject_from_return_value(return_value)
|
312
123
|
end
|
313
|
-
|
314
|
-
|
124
|
+
|
125
|
+
def verify_times_matcher_is_set
|
126
|
+
unless definition.times_matcher
|
127
|
+
raise RR::Errors::DoubleDefinitionError, "#definition.times_matcher is not set"
|
128
|
+
end
|
315
129
|
end
|
316
|
-
protected :argument_expectation=
|
317
130
|
|
318
|
-
def
|
319
|
-
|
131
|
+
def verify_argument_expectation_is_set
|
132
|
+
unless definition.argument_expectation
|
133
|
+
raise RR::Errors::DoubleDefinitionError, "#definition.argument_expectation is not set"
|
134
|
+
end
|
320
135
|
end
|
321
136
|
|
322
|
-
|
137
|
+
def verify_method_signature
|
138
|
+
raise RR::Errors::SubjectDoesNotImplementMethodError unless definition.subject.respond_to?(double_injection.send(:original_method_name))
|
139
|
+
raise RR::Errors::SubjectHasDifferentArityError unless arity_matches?
|
140
|
+
end
|
141
|
+
|
142
|
+
def subject_arity
|
143
|
+
definition.subject.method(double_injection.send(:original_method_name)).arity
|
144
|
+
end
|
145
|
+
|
146
|
+
def subject_accepts_only_varargs?
|
147
|
+
subject_arity == -1
|
148
|
+
end
|
149
|
+
|
150
|
+
def subject_accepts_varargs?
|
151
|
+
subject_arity < 0
|
152
|
+
end
|
153
|
+
|
154
|
+
def arity_matches?
|
155
|
+
return true if subject_accepts_only_varargs?
|
156
|
+
if subject_accepts_varargs?
|
157
|
+
return ((subject_arity * -1) - 1) <= args.size
|
158
|
+
else
|
159
|
+
return subject_arity == args.size
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def args
|
164
|
+
definition.argument_expectation.expected_arguments
|
165
|
+
end
|
166
|
+
|
323
167
|
def do_call_implementation_and_get_return_value(double_injection, *args, &block)
|
324
168
|
if definition.implementation_is_original_method?
|
325
169
|
if double_injection.object_has_original_method?
|
@@ -349,12 +193,20 @@ module RR
|
|
349
193
|
def extract_subject_from_return_value(return_value)
|
350
194
|
case return_value
|
351
195
|
when DoubleDefinitions::DoubleDefinition
|
352
|
-
return_value.
|
196
|
+
return_value.root_subject
|
353
197
|
when DoubleDefinitions::DoubleDefinitionCreatorProxy
|
354
|
-
return_value.__creator__.
|
198
|
+
return_value.__creator__.root_subject
|
355
199
|
else
|
356
200
|
return_value
|
357
201
|
end
|
358
202
|
end
|
203
|
+
|
204
|
+
def implementation
|
205
|
+
definition.implementation
|
206
|
+
end
|
207
|
+
|
208
|
+
def argument_expectation
|
209
|
+
definition.argument_expectation
|
210
|
+
end
|
359
211
|
end
|
360
|
-
end
|
212
|
+
end
|