rr 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/CHANGES +10 -0
  2. data/README.rdoc +56 -18
  3. data/Rakefile +1 -2
  4. data/lib/rr.rb +14 -5
  5. data/lib/rr/adapters/rr_methods.rb +11 -0
  6. data/lib/rr/adapters/rspec.rb +30 -0
  7. data/lib/rr/adapters/test_unit.rb +4 -0
  8. data/lib/rr/double.rb +79 -227
  9. data/lib/rr/double_definitions/child_double_definition_creator.rb +4 -0
  10. data/lib/rr/double_definitions/double_definition.rb +138 -4
  11. data/lib/rr/double_definitions/double_definition_creator.rb +18 -4
  12. data/lib/rr/double_definitions/double_definition_creator_proxy.rb +35 -3
  13. data/lib/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation.rb +17 -0
  14. data/lib/rr/double_definitions/strategies/scope/instance_of_class.rb +3 -0
  15. data/lib/rr/double_injection.rb +10 -6
  16. data/lib/rr/errors/spy_verification_errors/double_injection_not_found_error.rb +8 -0
  17. data/lib/rr/errors/spy_verification_errors/invocation_count_error.rb +8 -0
  18. data/lib/rr/errors/spy_verification_errors/spy_verification_error.rb +8 -0
  19. data/lib/rr/errors/subject_does_not_implement_method_error.rb +6 -0
  20. data/lib/rr/errors/subject_has_different_arity_error.rb +6 -0
  21. data/lib/rr/expectations/times_called_expectation.rb +11 -9
  22. data/lib/rr/recorded_calls.rb +103 -0
  23. data/lib/rr/space.rb +18 -8
  24. data/lib/rr/spy_verification.rb +48 -0
  25. data/lib/rr/spy_verification_proxy.rb +18 -0
  26. data/lib/rr/times_called_matchers/any_times_matcher.rb +2 -3
  27. data/lib/rr/times_called_matchers/at_least_matcher.rb +2 -3
  28. data/lib/rr/times_called_matchers/at_most_matcher.rb +2 -3
  29. data/lib/rr/times_called_matchers/times_called_matcher.rb +4 -4
  30. data/spec/core_spec_suite.rb +1 -0
  31. data/spec/high_level_spec.rb +151 -14
  32. data/spec/rr/adapters/rr_methods_space_spec.rb +2 -2
  33. data/spec/rr/double_definitions/child_double_definition_creator_spec.rb +9 -0
  34. data/spec/rr/double_definitions/double_definition_creator_proxy_spec.rb +91 -19
  35. data/spec/rr/double_definitions/double_definition_creator_spec.rb +7 -0
  36. data/spec/rr/double_definitions/double_definition_spec.rb +53 -10
  37. data/spec/rr/double_injection/double_injection_dispatching_spec.rb +14 -15
  38. data/spec/rr/double_injection/double_injection_verify_spec.rb +1 -1
  39. data/spec/rr/double_spec.rb +79 -445
  40. data/spec/rr/errors/rr_error_spec.rb +49 -47
  41. data/spec/rr/expectations/times_called_expectation/times_called_expectation_any_times_spec.rb +4 -3
  42. data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_least_spec.rb +3 -2
  43. data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_most_spec.rb +3 -2
  44. data/spec/rr/expectations/times_called_expectation/times_called_expectation_helper.rb +2 -2
  45. data/spec/rr/expectations/times_called_expectation/times_called_expectation_integer_spec.rb +3 -3
  46. data/spec/rr/expectations/times_called_expectation/times_called_expectation_proc_spec.rb +6 -5
  47. data/spec/rr/expectations/times_called_expectation/times_called_expectation_range_spec.rb +3 -2
  48. data/spec/rr/expectations/times_called_expectation/times_called_expectation_spec.rb +3 -2
  49. data/spec/rr/rspec/invocation_matcher_spec.rb +259 -0
  50. data/spec/rr/rspec/rspec_adapter_spec.rb +1 -1
  51. data/spec/rr/rspec/rspec_usage_spec.rb +43 -24
  52. data/spec/rr/space/hash_with_object_id_key_spec.rb +2 -2
  53. data/spec/rr/space/space_spec.rb +27 -9
  54. data/spec/rr/test_unit/test_unit_integration_test.rb +10 -0
  55. data/spec/spec_helper.rb +4 -1
  56. data/spec/spy_verification_spec.rb +129 -0
  57. 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 proxies. In the future, RR will
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 |m|
208
- m.system("cd #{RAILS_ENV}") {true}
209
- m.system("rake foo:bar") {true}
210
- m.system("rake baz") {true}
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
- * Pivotal Labs for sponsoring RR development
277
- * Nick Kallen for documentation suggestions, bug reports, and patches
278
- * Matthew O'Conner for patches
279
- * Nathan Sobo for various ideas and inspiration for cleaner and more expressive code
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
- * Aslak Hellesoy for Developing Rspec
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.6.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
@@ -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
@@ -20,6 +20,10 @@ module RR
20
20
  alias_method :teardown, :teardown_with_rr
21
21
  end
22
22
  end
23
+
24
+ def assert_received(subject, &block)
25
+ block.call(received(subject)).call
26
+ end
23
27
  end
24
28
  end
25
29
  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
- return true unless definition.times_matcher
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
- return true unless definition.times_matcher
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
- return false unless definition.times_matcher
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
- return [] unless argument_expectation
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
- times_called_expectation.matcher
94
+ definition.times_matcher
295
95
  end
296
96
 
297
- def times_called_expectation
298
- @times_called_expectation.matcher = definition.times_matcher
299
- @times_called_expectation
97
+ def formatted_name
98
+ self.class.formatted_name(method_name, expected_arguments)
300
99
  end
301
100
 
302
- def implementation
303
- definition.implementation
101
+ protected
102
+ def ordered?
103
+ definition.ordered?
104
+ end
105
+
106
+ def verbose?
107
+ definition.verbose?
304
108
  end
305
- def implementation=(value)
306
- definition.implementation = value
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 argument_expectation
311
- definition.argument_expectation
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
- def argument_expectation=(value)
314
- definition.argument_expectation = value
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 formatted_name
319
- self.class.formatted_name(method_name, expected_arguments)
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
- protected
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.subject
196
+ return_value.root_subject
353
197
  when DoubleDefinitions::DoubleDefinitionCreatorProxy
354
- return_value.__creator__.subject
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