rr 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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