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
@@ -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