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
@@ -40,67 +40,148 @@ module RR
|
|
40
40
|
@double_definition_creator = double_definition_creator
|
41
41
|
@subject = subject
|
42
42
|
end
|
43
|
+
|
44
|
+
attr_reader :argument_expectation
|
43
45
|
|
44
|
-
|
46
|
+
def root_subject
|
47
|
+
double_definition_creator.root_subject
|
48
|
+
end
|
49
|
+
|
50
|
+
module ArgumentDefinitionConstructionMethods
|
51
|
+
# Double#with sets the expectation that the Double will receive
|
52
|
+
# the passed in arguments.
|
53
|
+
#
|
54
|
+
# Passing in a block sets the return value.
|
55
|
+
#
|
56
|
+
# mock(subject).method_name.with(1, 2) {:return_value}
|
45
57
|
def with(*args, &return_value_block)
|
46
58
|
@argument_expectation = Expectations::ArgumentEqualityExpectation.new(*args)
|
47
59
|
install_method_callback return_value_block
|
48
60
|
self
|
49
61
|
end
|
50
62
|
|
63
|
+
# Double#with_any_args sets the expectation that the Double can receive
|
64
|
+
# any arguments.
|
65
|
+
#
|
66
|
+
# Passing in a block sets the return value.
|
67
|
+
#
|
68
|
+
# mock(subject).method_name.with_any_args {:return_value}
|
51
69
|
def with_any_args(&return_value_block)
|
52
70
|
@argument_expectation = Expectations::AnyArgumentExpectation.new
|
53
71
|
install_method_callback return_value_block
|
54
72
|
self
|
55
73
|
end
|
56
74
|
|
75
|
+
# Double#with_no_args sets the expectation that the Double will receive
|
76
|
+
# no arguments.
|
77
|
+
#
|
78
|
+
# Passing in a block sets the return value.
|
79
|
+
#
|
80
|
+
# mock(subject).method_name.with_no_args {:return_value}
|
57
81
|
def with_no_args(&return_value_block)
|
58
82
|
@argument_expectation = Expectations::ArgumentEqualityExpectation.new()
|
59
83
|
install_method_callback return_value_block
|
60
84
|
self
|
61
|
-
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
include ArgumentDefinitionConstructionMethods
|
62
88
|
|
89
|
+
module TimesDefinitionConstructionMethods
|
90
|
+
# Double#never sets the expectation that the Double will never be
|
91
|
+
# called.
|
92
|
+
#
|
93
|
+
# This method does not accept a block because it will never be called.
|
94
|
+
#
|
95
|
+
# mock(subject).method_name.never
|
63
96
|
def never
|
64
97
|
@times_matcher = TimesCalledMatchers::IntegerMatcher.new(0)
|
65
98
|
self
|
66
99
|
end
|
67
100
|
|
101
|
+
# Double#once sets the expectation that the Double will be called
|
102
|
+
# 1 time.
|
103
|
+
#
|
104
|
+
# Passing in a block sets the return value.
|
105
|
+
#
|
106
|
+
# mock(subject).method_name.once {:return_value}
|
68
107
|
def once(&return_value_block)
|
69
108
|
@times_matcher = TimesCalledMatchers::IntegerMatcher.new(1)
|
70
109
|
install_method_callback return_value_block
|
71
110
|
self
|
72
111
|
end
|
73
112
|
|
113
|
+
# Double#twice sets the expectation that the Double will be called
|
114
|
+
# 2 times.
|
115
|
+
#
|
116
|
+
# Passing in a block sets the return value.
|
117
|
+
#
|
118
|
+
# mock(subject).method_name.twice {:return_value}
|
74
119
|
def twice(&return_value_block)
|
75
120
|
@times_matcher = TimesCalledMatchers::IntegerMatcher.new(2)
|
76
121
|
install_method_callback return_value_block
|
77
122
|
self
|
78
123
|
end
|
79
124
|
|
125
|
+
# Double#at_least sets the expectation that the Double
|
126
|
+
# will be called at least n times.
|
127
|
+
# It works by creating a TimesCalledExpectation.
|
128
|
+
#
|
129
|
+
# Passing in a block sets the return value.
|
130
|
+
#
|
131
|
+
# mock(subject).method_name.at_least(4) {:return_value}
|
80
132
|
def at_least(number, &return_value_block)
|
81
133
|
@times_matcher = TimesCalledMatchers::AtLeastMatcher.new(number)
|
82
134
|
install_method_callback return_value_block
|
83
135
|
self
|
84
136
|
end
|
85
137
|
|
138
|
+
# Double#at_most allows sets the expectation that the Double
|
139
|
+
# will be called at most n times.
|
140
|
+
# It works by creating a TimesCalledExpectation.
|
141
|
+
#
|
142
|
+
# Passing in a block sets the return value.
|
143
|
+
#
|
144
|
+
# mock(subject).method_name.at_most(4) {:return_value}
|
86
145
|
def at_most(number, &return_value_block)
|
87
146
|
@times_matcher = TimesCalledMatchers::AtMostMatcher.new(number)
|
88
147
|
install_method_callback return_value_block
|
89
148
|
self
|
90
149
|
end
|
91
150
|
|
151
|
+
# Double#any_number_of_times sets an that the Double will be called
|
152
|
+
# any number of times. This effectively removes the times called expectation
|
153
|
+
# from the Doublen
|
154
|
+
#
|
155
|
+
# Passing in a block sets the return value.
|
156
|
+
#
|
157
|
+
# mock(subject).method_name.any_number_of_times
|
92
158
|
def any_number_of_times(&return_value_block)
|
93
159
|
@times_matcher = TimesCalledMatchers::AnyTimesMatcher.new
|
94
160
|
install_method_callback return_value_block
|
95
161
|
self
|
96
162
|
end
|
97
163
|
|
164
|
+
# Double#times creates an TimesCalledExpectation of the passed
|
165
|
+
# in number.
|
166
|
+
#
|
167
|
+
# Passing in a block sets the return value.
|
168
|
+
#
|
169
|
+
# mock(subject).method_name.times(4) {:return_value}
|
98
170
|
def times(matcher_value, &return_value_block)
|
99
171
|
@times_matcher = TimesCalledMatchers::TimesCalledMatcher.create(matcher_value)
|
100
172
|
install_method_callback return_value_block
|
101
173
|
self
|
102
174
|
end
|
175
|
+
end
|
176
|
+
include TimesDefinitionConstructionMethods
|
103
177
|
|
178
|
+
module DefinitionConstructionMethods
|
179
|
+
# Double#ordered sets the Double to have an ordered
|
180
|
+
# expectation.
|
181
|
+
#
|
182
|
+
# Passing in a block sets the return value.
|
183
|
+
#
|
184
|
+
# mock(subject).method_name.ordered {return_value}
|
104
185
|
def ordered(&return_value_block)
|
105
186
|
raise(
|
106
187
|
Errors::DoubleDefinitionError,
|
@@ -115,24 +196,53 @@ module RR
|
|
115
196
|
end
|
116
197
|
alias_method :then, :ordered
|
117
198
|
|
199
|
+
# Double#yields sets the Double to invoke a passed in block when
|
200
|
+
# the Double is called.
|
201
|
+
# An Expection will be raised if no block is passed in when the
|
202
|
+
# Double is called.
|
203
|
+
#
|
204
|
+
# Passing in a block sets the return value.
|
205
|
+
#
|
206
|
+
# mock(subject).method_name.yields(yield_arg1, yield_arg2) {return_value}
|
207
|
+
# subject.method_name {|yield_arg1, yield_arg2|}
|
118
208
|
def yields(*args, &return_value_block)
|
119
209
|
@yields_value = args
|
120
210
|
install_method_callback return_value_block
|
121
211
|
self
|
122
212
|
end
|
123
213
|
|
214
|
+
# Double#after_call creates a callback that occurs after call
|
215
|
+
# is called. The passed in block receives the return value of
|
216
|
+
# the Double being called.
|
217
|
+
# An Expection will be raised if no block is passed in.
|
218
|
+
#
|
219
|
+
# mock(subject).method_name {return_value}.after_call {|return_value|}
|
220
|
+
# subject.method_name # return_value
|
221
|
+
#
|
222
|
+
# This feature is built into proxies.
|
223
|
+
# mock.proxy(User).find('1') {|user| mock(user).valid? {false}}
|
124
224
|
def after_call(&after_call_proc)
|
125
225
|
raise ArgumentError, "after_call expects a block" unless after_call_proc
|
126
226
|
@after_call_proc = after_call_proc
|
127
227
|
self
|
128
228
|
end
|
129
229
|
|
230
|
+
# Double#verbose sets the Double to print out each method call it receives.
|
231
|
+
#
|
232
|
+
# Passing in a block sets the return value
|
130
233
|
def verbose(&after_call_proc)
|
131
234
|
@verbose = true
|
132
235
|
@after_call_proc = after_call_proc
|
133
236
|
self
|
134
237
|
end
|
135
238
|
|
239
|
+
# Double#returns accepts an argument value or a block.
|
240
|
+
# It will raise an ArgumentError if both are passed in.
|
241
|
+
#
|
242
|
+
# Passing in a block causes Double to return the return value of
|
243
|
+
# the passed in block.
|
244
|
+
#
|
245
|
+
# Passing in an argument causes Double to return the argument.
|
136
246
|
def returns(*args, &implementation)
|
137
247
|
value = args.first
|
138
248
|
if !args.empty? && implementation
|
@@ -151,11 +261,26 @@ module RR
|
|
151
261
|
self
|
152
262
|
end
|
153
263
|
|
264
|
+
# Double#implemented_by sets the implementation of the Double.
|
265
|
+
# This method takes a Proc or a Method. Passing in a Method allows
|
266
|
+
# the Double to accept blocks.
|
267
|
+
#
|
268
|
+
# obj = Object.new
|
269
|
+
# def obj.foobar
|
270
|
+
# yield(1)
|
271
|
+
# end
|
272
|
+
# mock(obj).method_name.implemented_by(obj.method(:foobar))
|
154
273
|
def implemented_by(implementation)
|
155
274
|
@implementation = implementation
|
156
275
|
self
|
157
276
|
end
|
158
277
|
|
278
|
+
def verify_method_signature
|
279
|
+
@verify_method_signature = true
|
280
|
+
self
|
281
|
+
end
|
282
|
+
alias_method :strong, :verify_method_signature
|
283
|
+
|
159
284
|
protected
|
160
285
|
def install_method_callback(block)
|
161
286
|
return unless block
|
@@ -169,10 +294,15 @@ module RR
|
|
169
294
|
include DefinitionConstructionMethods
|
170
295
|
|
171
296
|
module StateQueryMethods
|
297
|
+
# Double#ordered? returns true when the Double is ordered.
|
298
|
+
#
|
299
|
+
# mock(subject).method_name.ordered?
|
172
300
|
def ordered?
|
173
301
|
@ordered
|
174
302
|
end
|
175
303
|
|
304
|
+
# Double#verbose? returns true when verbose has been called on it. It returns
|
305
|
+
# true when the double is set to print each method call it receives.
|
176
306
|
def verbose?
|
177
307
|
@verbose ? true : false
|
178
308
|
end
|
@@ -193,14 +323,18 @@ module RR
|
|
193
323
|
end
|
194
324
|
|
195
325
|
def expected_arguments
|
196
|
-
|
197
|
-
argument_expectation.expected_arguments
|
326
|
+
argument_expectation ? argument_expectation.expected_arguments : []
|
198
327
|
end
|
199
328
|
|
200
329
|
def implementation_is_original_method?
|
201
330
|
implementation_strategy.is_a?(Strategies::Implementation::Proxy)
|
202
331
|
end
|
203
332
|
|
333
|
+
def verify_method_signature?
|
334
|
+
!!@verify_method_signature
|
335
|
+
end
|
336
|
+
alias_method :strong?, :verify_method_signature?
|
337
|
+
|
204
338
|
protected
|
205
339
|
def implementation_strategy
|
206
340
|
double_definition_creator.implementation_strategy
|
@@ -17,7 +17,7 @@ module RR
|
|
17
17
|
end
|
18
18
|
CLASS
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def register_implementation_strategy_class(strategy_class, method_name)
|
22
22
|
class_eval((<<-CLASS), __FILE__, __LINE__ + 1)
|
23
23
|
def #{method_name}(subject=NO_SUBJECT, method_name=nil, &definition_eval_block)
|
@@ -51,7 +51,13 @@ module RR
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
attr_reader :subject,
|
54
|
+
attr_reader :subject,
|
55
|
+
:method_name,
|
56
|
+
:args, :handler,
|
57
|
+
:definition,
|
58
|
+
:verification_strategy,
|
59
|
+
:implementation_strategy,
|
60
|
+
:scope_strategy
|
55
61
|
NO_SUBJECT = Object.new
|
56
62
|
|
57
63
|
include Space::Reader
|
@@ -62,6 +68,14 @@ module RR
|
|
62
68
|
@scope_strategy = Strategies::Scope::Instance.new(self)
|
63
69
|
end
|
64
70
|
|
71
|
+
def root_subject
|
72
|
+
subject
|
73
|
+
end
|
74
|
+
|
75
|
+
def method_name
|
76
|
+
@verification_strategy.method_name
|
77
|
+
end
|
78
|
+
|
65
79
|
module StrategySetupMethods
|
66
80
|
def no_subject?
|
67
81
|
subject.__id__ === NO_SUBJECT.__id__
|
@@ -89,7 +103,7 @@ module RR
|
|
89
103
|
@verification_strategy = verification_strategy
|
90
104
|
verification_strategy
|
91
105
|
end
|
92
|
-
|
106
|
+
|
93
107
|
def implementation_strategy=(implementation_strategy)
|
94
108
|
verify_not_proxy_and_dont_allow(verification_strategy, implementation_strategy)
|
95
109
|
@implementation_strategy = implementation_strategy
|
@@ -150,4 +164,4 @@ module RR
|
|
150
164
|
end
|
151
165
|
end
|
152
166
|
end
|
153
|
-
end
|
167
|
+
end
|
@@ -4,17 +4,49 @@ module RR
|
|
4
4
|
def initialize(creator, &block) #:nodoc:
|
5
5
|
@creator = creator
|
6
6
|
class << self
|
7
|
+
def __apply_blank_slate
|
8
|
+
@apply_blank_slate = true
|
9
|
+
end
|
10
|
+
|
11
|
+
def __apply_blank_slate?
|
12
|
+
@apply_blank_slate ||= false
|
13
|
+
end
|
14
|
+
|
7
15
|
instance_methods.each do |m|
|
8
|
-
unless m =~ /^_/ || m.to_s == 'object_id'
|
16
|
+
unless m =~ /^_/ || m.to_s == 'object_id' || m.to_s == "instance_eval" || m.to_s == 'respond_to?'
|
17
|
+
alias_method "__blank_slated_#{m}", m
|
9
18
|
undef_method m
|
10
19
|
end
|
11
20
|
end
|
12
21
|
|
22
|
+
def instance_eval
|
23
|
+
return_value = super
|
24
|
+
class << self
|
25
|
+
alias_method "__blank_slated_instance_eval", "instance_eval"
|
26
|
+
undef_method :instance_eval
|
27
|
+
alias_method "__blank_slated_respond_to?", "respond_to?"
|
28
|
+
undef_method :respond_to?
|
29
|
+
end
|
30
|
+
return_value
|
31
|
+
end
|
32
|
+
|
13
33
|
def method_missing(method_name, *args, &block)
|
14
|
-
|
34
|
+
if __apply_blank_slate?
|
35
|
+
@creator.create(method_name, *args, &block)
|
36
|
+
else
|
37
|
+
__blank_slated_send("__blank_slated_#{method_name}", *args, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
__apply_blank_slate
|
43
|
+
if block_given?
|
44
|
+
if block.arity == 1
|
45
|
+
yield(self)
|
46
|
+
else
|
47
|
+
instance_eval(&block)
|
15
48
|
end
|
16
49
|
end
|
17
|
-
yield(self) if block_given?
|
18
50
|
end
|
19
51
|
|
20
52
|
def __creator__
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RR
|
2
|
+
module DoubleDefinitions
|
3
|
+
module Strategies
|
4
|
+
module Implementation
|
5
|
+
class StronglyTypedReimplementation < Reimplementation
|
6
|
+
register("strong")
|
7
|
+
|
8
|
+
protected
|
9
|
+
def do_call
|
10
|
+
super
|
11
|
+
definition.verify_method_signature
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -27,12 +27,15 @@ module RR
|
|
27
27
|
protected
|
28
28
|
def do_call
|
29
29
|
class_handler = lambda do |return_value|
|
30
|
+
#####
|
30
31
|
double_injection = space.double_injection(return_value, method_name)
|
31
32
|
Double.new(double_injection, definition)
|
33
|
+
#####
|
32
34
|
return_value
|
33
35
|
end
|
34
36
|
|
35
37
|
instance_of_subject_creator = DoubleDefinitionCreator.new
|
38
|
+
instance_of_subject_creator.strong if definition.verify_method_signature?
|
36
39
|
instance_of_subject_creator.stub.proxy(subject)
|
37
40
|
instance_of_subject_creator.create(:new, &class_handler)
|
38
41
|
end
|
data/lib/rr/double_injection.rb
CHANGED
@@ -3,6 +3,7 @@ module RR
|
|
3
3
|
# A double_injection has 0 to many Double objects. Each Double
|
4
4
|
# has Argument Expectations and Times called Expectations.
|
5
5
|
class DoubleInjection
|
6
|
+
include Space::Reader
|
6
7
|
MethodArguments = Struct.new(:arguments, :block)
|
7
8
|
attr_reader :subject, :method_name, :doubles
|
8
9
|
|
@@ -38,6 +39,7 @@ module RR
|
|
38
39
|
end
|
39
40
|
METHOD
|
40
41
|
meta.class_eval(returns_method, __FILE__, __LINE__ - 5)
|
42
|
+
self
|
41
43
|
end
|
42
44
|
|
43
45
|
# RR::DoubleInjection#verify verifies each Double
|
@@ -78,6 +80,7 @@ module RR
|
|
78
80
|
end
|
79
81
|
|
80
82
|
def call_method(args, block)
|
83
|
+
space.record_call(subject, method_name, args, block)
|
81
84
|
if double = find_double_to_attempt(args)
|
82
85
|
double.call(self, *args, &block)
|
83
86
|
else
|
@@ -112,11 +115,12 @@ module RR
|
|
112
115
|
end
|
113
116
|
|
114
117
|
def double_not_found_error(*args)
|
115
|
-
message =
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
118
|
+
message =
|
119
|
+
"On subject #{subject},\n" <<
|
120
|
+
"unexpected method invocation:\n" <<
|
121
|
+
" #{Double.formatted_name(@method_name, args)}\n" <<
|
122
|
+
"expected invocations:\n" <<
|
123
|
+
Double.list_message_part(@doubles)
|
120
124
|
raise Errors::DoubleNotFoundError, message
|
121
125
|
end
|
122
126
|
|
@@ -136,4 +140,4 @@ module RR
|
|
136
140
|
(class << @subject; self; end)
|
137
141
|
end
|
138
142
|
end
|
139
|
-
end
|
143
|
+
end
|