rr 0.1.5 → 0.1.6

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 CHANGED
@@ -1,3 +1,6 @@
1
+ * 0.1.6
2
+ - [#12120] probe allows a the return value to be intercepted
3
+
1
4
  * 0.1.5
2
5
  - TimesCalledExpectation says how many times were called and how many times called were expected on error
3
6
 
data/README CHANGED
@@ -25,6 +25,13 @@ renders the partial.
25
25
 
26
26
  view = controller.template
27
27
  probe(view).render(:partial => "user_info")
28
+
29
+ Probes also support after_call callbacks. This is useful for Stubbing out
30
+ a class method and getting its return value. For example, using ActiveRecord:
31
+
32
+ probe(User).find('5') do |user|
33
+ mock(user).valid? {false}
34
+ end
28
35
 
29
36
  == Block Syntax
30
37
  script = MyScript.new
data/Rakefile CHANGED
@@ -21,7 +21,7 @@ def run_suite
21
21
  end
22
22
 
23
23
  PKG_NAME = "rr"
24
- PKG_VERSION = "0.1.5"
24
+ PKG_VERSION = "0.1.6"
25
25
  PKG_FILES = FileList[
26
26
  '[A-Z]*',
27
27
  '*.rb',
@@ -78,6 +78,18 @@ describe ProbeCreator, "#method_missing" do
78
78
  @subject.foobar(1, 2).should == :baz
79
79
  proc {@subject.foobar(1, 2)}.should raise_error(Errors::TimesCalledError)
80
80
  end
81
+
82
+ it "sets after_call on the scenario when passed a block" do
83
+ real_value = Object.new
84
+ (class << @subject; self; end).class_eval do
85
+ define_method(:foobar) {real_value}
86
+ end
87
+ @creator.foobar(1, 2) {|value| mock(value).a_method {99}}
88
+
89
+ return_value = @subject.foobar(1, 2)
90
+ return_value.should === return_value
91
+ return_value.a_method.should == 99
92
+ end
81
93
  end
82
94
 
83
95
  end
@@ -195,6 +195,40 @@ describe Scenario, "#yields" do
195
195
  end
196
196
  end
197
197
 
198
+ describe Scenario, "#after_call" do
199
+ it_should_behave_like "RR::Scenario"
200
+
201
+ it "returns self" do
202
+ @scenario.after_call {}.should === @scenario
203
+ end
204
+
205
+ it "receives the return value in the block" do
206
+ return_value = {}
207
+ @scenario.returns(return_value).after_call do |value|
208
+ value[:foo] = :bar
209
+ end
210
+
211
+ actual_value = @scenario.call
212
+ actual_value.should === return_value
213
+ actual_value.should == {:foo => :bar}
214
+ end
215
+
216
+ it "allows after_call to mock the return value" do
217
+ return_value = Object.new
218
+ @scenario.with_any_args.returns(return_value).after_call do |value|
219
+ mock(value).inner_method(1) {:baz}
220
+ end
221
+
222
+ @object.foobar.inner_method(1).should == :baz
223
+ end
224
+
225
+ it "raises an error when not passed a block" do
226
+ proc do
227
+ @scenario.after_call
228
+ end.should raise_error(ArgumentError, "after_call expects a block")
229
+ end
230
+ end
231
+
198
232
  describe Scenario, "#returns" do
199
233
  it_should_behave_like "RR::Scenario"
200
234
 
@@ -3,15 +3,24 @@ module RR
3
3
  # a Scenario that acts like a probe.
4
4
  #
5
5
  # The following example probes method_name with arg1 and arg2
6
- # returning return_value.
6
+ # returning the actual value of the method. The block is an after callback
7
+ # that intercepts the return value. Mocks or other modifications can
8
+ # be done to the return value.
7
9
  #
8
- # probe(subject).method_name(arg1, arg2) { return_value }
10
+ # probe(subject).method_name(arg1, arg2) { |return_value| }
9
11
  #
10
- # The ProbeCreator also supports a block sytnax.
12
+ # The ProbeCreator also supports a block sytnax. The block accepts
13
+ # a after_call callback, instead of a return value as with MockCreator
14
+ # and StubCreator.
11
15
  #
12
- # probe(subject) do |m|
13
- # m.method_name(arg1, arg2) { return_value }
16
+ # probe(User) do |m|
17
+ # m.find('4') do |user|
18
+ # mock(user).valid? {false}
19
+ # end
14
20
  # end
21
+ #
22
+ # user = User.find('4')
23
+ # user.valid? # false
15
24
  class ProbeCreator
16
25
  instance_methods.each { |m| undef_method m unless m =~ /^__/ }
17
26
 
@@ -22,10 +31,12 @@ module RR
22
31
  end
23
32
 
24
33
  protected
25
- def method_missing(method_name, *args, &returns)
34
+ def method_missing(method_name, *args, &after_call)
26
35
  double = @space.create_double(@subject, method_name)
27
36
  scenario = @space.create_scenario(double)
28
37
  scenario.with(*args).once.implemented_by(double.original_method)
38
+ scenario.after_call(&after_call) if after_call
39
+ scenario
29
40
  end
30
41
  end
31
42
  end
data/lib/rr/scenario.rb CHANGED
@@ -11,6 +11,7 @@ module RR
11
11
  @argument_expectation = nil
12
12
  @times_called_expectation = nil
13
13
  @times_called = 0
14
+ @after_call = nil
14
15
  @yields = nil
15
16
  end
16
17
 
@@ -114,6 +115,37 @@ module RR
114
115
  @ordered
115
116
  end
116
117
 
118
+ # Scenario#yields sets the Scenario to invoke a passed in block when
119
+ # the Scenario is called.
120
+ # An Expection will be raised if no block is passed in when the
121
+ # Scenario is called.
122
+ #
123
+ # Passing in a block sets the return value.
124
+ #
125
+ # mock(subject).method_name.yields(yield_arg1, yield_arg2) {return_value}
126
+ # subject.method_name {|yield_arg1, yield_arg2|}
127
+ def yields(*args, &returns)
128
+ @yields = args
129
+ returns(&returns) if returns
130
+ self
131
+ end
132
+
133
+ # Scenario#after_call creates a callback that occurs after call
134
+ # is called. The passed in block receives the return value of
135
+ # the Scenario being called.
136
+ # An Expection will be raised if no block is passed in.
137
+ #
138
+ # mock(subject).method_name {return_value}.after_call {|return_value|}
139
+ # subject.method_name # return_value
140
+ #
141
+ # This feature is built into probes.
142
+ # probe(User).find('1') {|user| mock(user).valid? {false}}
143
+ def after_call(&block)
144
+ raise ArgumentError, "after_call expects a block" unless block
145
+ @after_call = block
146
+ self
147
+ end
148
+
117
149
  # Scenario#returns accepts an argument value or a block.
118
150
  # It will raise an ArgumentError if both are passed in.
119
151
  #
@@ -130,21 +162,6 @@ module RR
130
162
  end
131
163
  end
132
164
 
133
- # Scenario#yields sets the Scenario to invoke a passed in block when
134
- # the Scenario is called.
135
- # An Expection will be raised if no block is passed in when the
136
- # Scenario is called.
137
- #
138
- # Passing in a block sets the return value.
139
- #
140
- # mock(subject).method_name.yields(yield_arg1, yield_arg2) {return_value}
141
- # subject.method_name {|yield_arg1, yield_arg2|}
142
- def yields(*args, &returns)
143
- @yields = args
144
- returns(&returns) if returns
145
- self
146
- end
147
-
148
165
  # Scenario#implemented_by sets the implementation of the Scenario.
149
166
  # This method takes a Proc or a Method. Passing in a Method allows
150
167
  # the Scenario to accept blocks.
@@ -165,6 +182,12 @@ module RR
165
182
  # A TimesCalledError is raised when the times called
166
183
  # exceeds the expected TimesCalledExpectation.
167
184
  def call(*args, &block)
185
+ return_value = call_implementation(*args, &block)
186
+ @after_call.call(return_value) if @after_call
187
+ return_value
188
+ end
189
+
190
+ def call_implementation(*args, &block)
168
191
  @times_called_expectation.verify_input if @times_called_expectation
169
192
  @space.verify_ordered_scenario(self) if ordered?
170
193
  if @yields
@@ -182,6 +205,7 @@ module RR
182
205
  return @implementation.call(*args)
183
206
  end
184
207
  end
208
+ protected :call_implementation
185
209
 
186
210
  # Scenario#exact_match? returns true when the passed in arguments
187
211
  # exactly match the ArgumentEqualityError arguments.
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.3
3
3
  specification_version: 1
4
4
  name: rr
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.5
7
- date: 2007-07-09 00:00:00 -07:00
6
+ version: 0.1.6
7
+ date: 2007-07-10 00:00:00 -07:00
8
8
  summary: RR (Double Ruby) is a double framework that features a rich selection of double techniques and a terse syntax. http://xunitpatterns.com/Test%20Double.html
9
9
  require_paths:
10
10
  - lib