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.
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
@@ -7,6 +7,10 @@ module RR
7
7
  super()
8
8
  end
9
9
 
10
+ def root_subject
11
+ parent_double_definition.root_subject
12
+ end
13
+
10
14
  def instance_of(*args)
11
15
  raise NoMethodError
12
16
  end
@@ -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
- module DefinitionConstructionMethods
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
- return [] unless argument_expectation
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, :method_name, :args, :handler, :definition, :verification_strategy, :implementation_strategy, :scope_strategy
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
- @creator.create(method_name, *args, &block)
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
@@ -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 = "On subject #{subject},\n"
116
- message << "unexpected method invocation:\n"
117
- message << " #{Double.formatted_name(@method_name, args)}\n"
118
- message << "expected invocations:\n"
119
- message << Double.list_message_part(@doubles)
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