rr 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGES +5 -0
  2. data/README +20 -9
  3. data/Rakefile +1 -1
  4. data/examples/example_suite.rb +1 -1
  5. data/examples/high_level_example.rb +4 -4
  6. data/examples/rr/double/double_dispatching_example.rb +41 -41
  7. data/examples/rr/double/double_verify_example.rb +1 -1
  8. data/examples/rr/expectations/times_called_expectation/times_called_expectation_helper.rb +2 -2
  9. data/examples/rr/extensions/instance_methods_creator_example.rb +48 -38
  10. data/examples/rr/extensions/instance_methods_space_example.rb +8 -8
  11. data/examples/rr/rspec/rspec_adapter_example.rb +9 -9
  12. data/examples/rr/rspec/rspec_usage_example.rb +16 -2
  13. data/examples/rr/scenario_creator_example.rb +400 -0
  14. data/examples/rr/scenario_example.rb +19 -4
  15. data/examples/rr/scenario_method_proxy_example.rb +71 -0
  16. data/examples/rr/space/space_create_example.rb +93 -106
  17. data/examples/rr/space/space_example.rb +2 -2
  18. data/examples/rr/space/space_register_example.rb +3 -3
  19. data/examples/rr/space/space_reset_example.rb +11 -11
  20. data/examples/rr/space/space_verify_example.rb +12 -12
  21. data/examples/rr/test_unit/test_unit_integration_test.rb +11 -2
  22. data/lib/rr.rb +10 -12
  23. data/lib/rr/errors/scenario_definition_error.rb +6 -0
  24. data/lib/rr/extensions/instance_methods.rb +84 -84
  25. data/lib/rr/scenario.rb +18 -3
  26. data/lib/rr/scenario_creator.rb +233 -0
  27. data/lib/rr/scenario_method_proxy.rb +19 -0
  28. data/lib/rr/space.rb +12 -32
  29. metadata +7 -13
  30. data/examples/rr/do_not_allow_creator_example.rb +0 -111
  31. data/examples/rr/mock_creator_example.rb +0 -87
  32. data/examples/rr/mock_probe_creator_example.rb +0 -120
  33. data/examples/rr/stub_creator_example.rb +0 -96
  34. data/examples/rr/stub_probe_creator_example.rb +0 -127
  35. data/lib/rr/creator.rb +0 -16
  36. data/lib/rr/do_not_allow_creator.rb +0 -33
  37. data/lib/rr/mock_creator.rb +0 -26
  38. data/lib/rr/mock_probe_creator.rb +0 -36
  39. data/lib/rr/stub_creator.rb +0 -30
  40. data/lib/rr/stub_probe_creator.rb +0 -42
@@ -20,12 +20,12 @@ module RR
20
20
 
21
21
  def initialize(space, double)
22
22
  @space = space
23
+ @double = double
23
24
  @implementation = nil
24
25
  @argument_expectation = nil
25
26
  @times_called_expectation = nil
26
27
  @times_called = 0
27
28
  @after_call = nil
28
- @double = double
29
29
  @yields = nil
30
30
  end
31
31
 
@@ -203,7 +203,7 @@ module RR
203
203
  @after_call = block
204
204
  self
205
205
  end
206
-
206
+
207
207
  # Scenario#returns accepts an argument value or a block.
208
208
  # It will raise an ArgumentError if both are passed in.
209
209
  #
@@ -236,6 +236,21 @@ module RR
236
236
  self
237
237
  end
238
238
 
239
+ # Scenario#implemented_by_original_method sets the implementation
240
+ # of the Scenario to be the original method.
241
+ # This is primarily used with probes.
242
+ #
243
+ # obj = Object.new
244
+ # def obj.foobar
245
+ # yield(1)
246
+ # end
247
+ # mock(obj).method_name.implemented_by_original_method
248
+ # obj.foobar {|arg| puts arg} # puts 1
249
+ def implemented_by_original_method
250
+ implemented_by @double.original_method
251
+ self
252
+ end
253
+
239
254
  # Scenario#call calls the Scenario's implementation. The return
240
255
  # value of the implementation is returned.
241
256
  #
@@ -270,7 +285,7 @@ module RR
270
285
  # Scenario#exact_match? returns true when the passed in arguments
271
286
  # exactly match the ArgumentEqualityExpectation arguments.
272
287
  def exact_match?(*arguments)
273
- return false unless @argument_expectation
288
+ return false unless @argument_expectation
274
289
  @argument_expectation.exact_match?(*arguments)
275
290
  end
276
291
 
@@ -0,0 +1,233 @@
1
+ module RR
2
+ # RR::ScenarioCreator provides a strategies to create a Scenario.
3
+ # The strategies are:
4
+ # * mock
5
+ # * stub
6
+ # * do_not_call
7
+ #
8
+ # Probing can also be added.
9
+ class ScenarioCreator
10
+ NO_SUBJECT_ARG = Object.new
11
+
12
+ attr_reader :space, :subject
13
+ include Errors
14
+
15
+ def initialize(space)
16
+ @space = space
17
+ @strategy = nil
18
+ @probe = false
19
+ end
20
+
21
+ def create!(subject, method_name, *args, &handler)
22
+ @subject = subject
23
+ @method_name = method_name
24
+ @args = args
25
+ @handler = handler
26
+ @double = @space.double(@subject, method_name)
27
+ @scenario = @space.scenario(@double)
28
+ transform!
29
+ @scenario
30
+ end
31
+
32
+ # This method sets the Scenario to have a mock strategy. A mock strategy
33
+ # sets the default state of the Scenario to expect the method call
34
+ # with arguments exactly one time. The Scenario's expectations can be
35
+ # changed.
36
+ #
37
+ # This method can be chained with probe.
38
+ # mock.probe(subject).method_name_1
39
+ # or
40
+ # probe.mock(subject).method_name_1
41
+ #
42
+ # When passed the subject, a ScenarioMethodProxy is returned. Passing
43
+ # a method with arguments to the proxy will set up expectations that
44
+ # the a call to the subject's method with the arguments will happen.
45
+ # mock(subject).method_name_1 {return_value_1}
46
+ # mock(subject).method_name_2(arg1, arg2) {return_value_2}
47
+ #
48
+ # When passed the subject and the method_name, this method returns
49
+ # a mock Scenario with the method already set.
50
+ #
51
+ # mock(subject, :method_name_1) {return_value_1}
52
+ # mock(subject, :method_name_2).with(arg1, arg2) {return_value_2}
53
+ #
54
+ # mock also takes a block for definitions.
55
+ # mock(subject) do
56
+ # method_name_1 {return_value_1}
57
+ # method_name_2(arg_1, arg_2) {return_value_2}
58
+ # end
59
+ def mock(subject=NO_SUBJECT_ARG, method_name=nil, &definition)
60
+ strategy_error! if @strategy
61
+ @strategy = :mock
62
+ return self if subject === NO_SUBJECT_ARG
63
+ RR::Space.scenario_method_proxy(self, subject, method_name, &definition)
64
+ end
65
+
66
+ # This method sets the Scenario to have a stub strategy. A stub strategy
67
+ # sets the default state of the Scenario to expect the method call
68
+ # with any arguments any number of times. The Scenario's
69
+ # expectations can be changed.
70
+ #
71
+ # This method can be chained with probe.
72
+ # stub.probe(subject).method_name_1
73
+ # or
74
+ # probe.stub(subject).method_name_1
75
+ #
76
+ # When passed the subject, a ScenarioMethodProxy is returned. Passing
77
+ # a method with arguments to the proxy will set up expectations that
78
+ # the a call to the subject's method with the arguments will happen,
79
+ # and return the prescribed value.
80
+ # stub(subject).method_name_1 {return_value_1}
81
+ # stub(subject).method_name_2(arg_1, arg_2) {return_value_2}
82
+ #
83
+ # When passed the subject and the method_name, this method returns
84
+ # a stub Scenario with the method already set.
85
+ #
86
+ # mock(subject, :method_name_1) {return_value_1}
87
+ # mock(subject, :method_name_2).with(arg1, arg2) {return_value_2}
88
+ #
89
+ # stub also takes a block for definitions.
90
+ # stub(subject) do
91
+ # method_name_1 {return_value_1}
92
+ # method_name_2(arg_1, arg_2) {return_value_2}
93
+ # end
94
+ def stub(subject=NO_SUBJECT_ARG, method_name=nil, &definition)
95
+ strategy_error! if @strategy
96
+ @strategy = :stub
97
+ return self if subject === NO_SUBJECT_ARG
98
+ RR::Space.scenario_method_proxy(self, subject, method_name, &definition)
99
+ end
100
+
101
+ def do_not_call(subject=NO_SUBJECT_ARG, method_name=nil, &definition)
102
+ strategy_error! if @strategy
103
+ probe_when_do_not_call_error! if @probe
104
+ @strategy = :do_not_call
105
+ return self if subject === NO_SUBJECT_ARG
106
+ RR::Space.scenario_method_proxy(self, subject, method_name, &definition)
107
+ end
108
+ alias_method :dont_call, :do_not_call
109
+ alias_method :do_not_allow, :do_not_call
110
+ alias_method :dont_allow, :do_not_call
111
+
112
+ # This method add probe capabilities to the Scenario. probe can be called
113
+ # with mock or stub.
114
+ #
115
+ # mock.probe(controller.template).render(:partial => "my/socks")
116
+ #
117
+ # stub.probe(controller.template).render(:partial => "my/socks") do |html|
118
+ # html.should include("My socks are wet")
119
+ # html
120
+ # end
121
+ #
122
+ # mock.probe(controller.template).render(:partial => "my/socks") do |html|
123
+ # html.should include("My socks are wet")
124
+ # "My new return value"
125
+ # end
126
+ #
127
+ # mock.probe also takes a block for definitions.
128
+ # mock.probe(subject) do
129
+ # render(:partial => "my/socks")
130
+ #
131
+ # render(:partial => "my/socks") do |html|
132
+ # html.should include("My socks are wet")
133
+ # html
134
+ # end
135
+ #
136
+ # render(:partial => "my/socks") do |html|
137
+ # html.should include("My socks are wet")
138
+ # html
139
+ # end
140
+ #
141
+ # render(:partial => "my/socks") do |html|
142
+ # html.should include("My socks are wet")
143
+ # "My new return value"
144
+ # end
145
+ # end
146
+ #
147
+ # Passing a block to the Scenario (after the method name and arguments)
148
+ # allows you to intercept the return value.
149
+ # The return value can be modified, validated, and/or overridden by
150
+ # passing in a block. The return value of the block will replace
151
+ # the actual return value.
152
+ #
153
+ # mock.probe(controller.template).render(:partial => "my/socks") do |html|
154
+ # html.should include("My socks are wet")
155
+ # "My new return value"
156
+ # end
157
+ def probe(subject=NO_SUBJECT_ARG, method_name=nil, &definition)
158
+ probe_when_do_not_call_error! if @strategy == :do_not_call
159
+ @probe = true
160
+ return self if subject === NO_SUBJECT_ARG
161
+ RR::Space.scenario_method_proxy(self, subject, method_name, &definition)
162
+ end
163
+
164
+ protected
165
+ def transform!
166
+ case @strategy
167
+ when :mock; mock!
168
+ when :stub; stub!
169
+ when :do_not_call; do_not_call!
170
+ else no_strategy_error!
171
+ end
172
+
173
+ if @probe
174
+ probe!
175
+ else
176
+ reimplementation!
177
+ end
178
+ end
179
+
180
+ def mock!
181
+ @scenario.with(*@args).once
182
+ end
183
+
184
+ def stub!
185
+ @scenario.any_number_of_times
186
+ permissive_argument!
187
+ end
188
+
189
+ def do_not_call!
190
+ @scenario.never
191
+ permissive_argument!
192
+ reimplementation!
193
+ end
194
+
195
+ def permissive_argument!
196
+ if @args.empty?
197
+ @scenario.with_any_args
198
+ else
199
+ @scenario.with(*@args)
200
+ end
201
+ end
202
+
203
+ def reimplementation!
204
+ @scenario.returns(&@handler)
205
+ end
206
+
207
+ def probe!
208
+ @scenario.implemented_by_original_method
209
+ @scenario.after_call(&@handler) if @handler
210
+ end
211
+
212
+ def strategy_error!
213
+ raise(
214
+ ScenarioDefinitionError,
215
+ "This Scenario already has a #{@strategy} strategy"
216
+ )
217
+ end
218
+
219
+ def no_strategy_error!
220
+ raise(
221
+ ScenarioDefinitionError,
222
+ "This Scenario has no strategy"
223
+ )
224
+ end
225
+
226
+ def probe_when_do_not_call_error!
227
+ raise(
228
+ ScenarioDefinitionError,
229
+ "Scenarios cannot be probed when using do_not_call strategy"
230
+ )
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,19 @@
1
+ module RR
2
+ class ScenarioMethodProxy
3
+ def initialize(space, creator, object, &block)
4
+ @space = space
5
+ @creator = creator
6
+ @object = object
7
+ class << self
8
+ instance_methods.each do |m|
9
+ undef_method m unless m =~ /^__/
10
+ end
11
+
12
+ def method_missing(method_name, *args, &block)
13
+ @creator.create!(@object, method_name, *args, &block)
14
+ end
15
+ end
16
+ yield(self) if block_given?
17
+ end
18
+ end
19
+ end
@@ -25,33 +25,22 @@ module RR
25
25
  @trim_backtrace = false
26
26
  end
27
27
 
28
- # Creates a MockCreator.
29
- def mock_creator(subject, method_name=nil, &definition)
30
- setup_creator MockCreator, subject, method_name, definition
31
- end
32
-
33
- # Creates a StubCreator.
34
- def stub_creator(subject, method_name=nil, &definition)
35
- setup_creator StubCreator, subject, method_name, definition
36
- end
37
-
38
- # Creates a MockProbeCreator.
39
- def mock_probe_creator(subject, method_name=nil, &definition)
40
- setup_creator MockProbeCreator, subject, method_name, definition
41
- end
42
-
43
- # Creates a StubProbeCreator.
44
- def stub_probe_creator(subject, method_name=nil, &definition)
45
- setup_creator StubProbeCreator, subject, method_name, definition
28
+ def scenario_method_proxy(creator, object, method_name=nil, &definition)
29
+ if method_name && definition
30
+ raise ArgumentError, "Cannot pass in a method name and a block"
31
+ end
32
+ proxy = ScenarioMethodProxy.new(self, creator, object, &definition)
33
+ return proxy unless method_name
34
+ proxy.__send__(method_name)
46
35
  end
47
36
 
48
- # Creates a DoNotAllowCreator.
49
- def do_not_allow_creator(subject, method_name=nil, &definition)
50
- setup_creator DoNotAllowCreator, subject, method_name, definition
37
+ # Creates a ScenarioCreator.
38
+ def scenario_creator
39
+ ScenarioCreator.new(self)
51
40
  end
52
41
 
53
42
  # Creates and registers a Scenario to be verified.
54
- def create_scenario(double)
43
+ def scenario(double)
55
44
  scenario = Scenario.new(self, double)
56
45
  double.register_scenario scenario
57
46
  scenario
@@ -61,7 +50,7 @@ module RR
61
50
  # in object and method_name.
62
51
  # When a Double is created, it binds the dispatcher to the
63
52
  # object.
64
- def create_double(object, method_name)
53
+ def double(object, method_name)
65
54
  double = @doubles[object][method_name.to_sym]
66
55
  return double if double
67
56
 
@@ -124,15 +113,6 @@ module RR
124
113
  end
125
114
 
126
115
  protected
127
- def setup_creator(klass, subject, method_name, definition)
128
- if method_name && definition
129
- raise ArgumentError, "Cannot pass in a method name and a block"
130
- end
131
- creator = klass.new(self, subject, &definition)
132
- return creator unless method_name
133
- creator.__send__(method_name)
134
- end
135
-
136
116
  # Removes the ordered Scenarios from the list
137
117
  def reset_ordered_scenarios
138
118
  @ordered_scenarios.clear
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.2.5
7
- date: 2007-07-21 00:00:00 -07:00
6
+ version: 0.3.0
7
+ date: 2007-07-22 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
@@ -33,17 +33,13 @@ files:
33
33
  - CHANGES
34
34
  - README
35
35
  - lib/rr.rb
36
- - lib/rr/mock_probe_creator.rb
36
+ - lib/rr/scenario_method_proxy.rb
37
37
  - lib/rr/scenario.rb
38
38
  - lib/rr/hash_with_object_id_key.rb
39
- - lib/rr/creator.rb
40
- - lib/rr/stub_probe_creator.rb
41
39
  - lib/rr/scenario_matches.rb
42
- - lib/rr/stub_creator.rb
43
40
  - lib/rr/space.rb
44
41
  - lib/rr/double.rb
45
- - lib/rr/do_not_allow_creator.rb
46
- - lib/rr/mock_creator.rb
42
+ - lib/rr/scenario_creator.rb
47
43
  - lib/rr/times_called_matchers/any_times_matcher.rb
48
44
  - lib/rr/times_called_matchers/at_most_matcher.rb
49
45
  - lib/rr/times_called_matchers/times_called_matcher.rb
@@ -61,6 +57,7 @@ files:
61
57
  - lib/rr/errors/scenario_order_error.rb
62
58
  - lib/rr/errors/rr_error.rb
63
59
  - lib/rr/errors/times_called_error.rb
60
+ - lib/rr/errors/scenario_definition_error.rb
64
61
  - lib/rr/adapters/test_unit.rb
65
62
  - lib/rr/adapters/rspec.rb
66
63
  - lib/rr/wildcard_matchers/boolean.rb
@@ -77,12 +74,9 @@ files:
77
74
  - examples/example_helper.rb
78
75
  - examples/test_unit_example_suite.rb
79
76
  - examples/high_level_example.rb
80
- - examples/rr/stub_probe_creator_example.rb
81
- - examples/rr/mock_creator_example.rb
82
- - examples/rr/stub_creator_example.rb
77
+ - examples/rr/scenario_method_proxy_example.rb
83
78
  - examples/rr/scenario_example.rb
84
- - examples/rr/do_not_allow_creator_example.rb
85
- - examples/rr/mock_probe_creator_example.rb
79
+ - examples/rr/scenario_creator_example.rb
86
80
  - examples/rr/rspec/rspec_backtrace_tweaking_example.rb
87
81
  - examples/rr/rspec/rspec_adapter_example.rb
88
82
  - examples/rr/rspec/rspec_usage_example.rb
@@ -1,111 +0,0 @@
1
- require "examples/example_helper"
2
-
3
- module RR
4
- describe DoNotAllowCreator, :shared => true do
5
- before(:each) do
6
- @space = Space.new
7
- @subject = Object.new
8
- end
9
-
10
- it "initializes creator with passed in object" do
11
- class << @creator
12
- attr_reader :subject
13
- end
14
- @creator.subject.should === @subject
15
- end
16
- end
17
-
18
- describe DoNotAllowCreator, ".new" do
19
- it_should_behave_like "RR::DoNotAllowCreator"
20
-
21
- before do
22
- @creator = DoNotAllowCreator.new(@space, @subject)
23
- end
24
-
25
- it "clears out all methods from creator" do
26
- creator_subclass = Class.new(DoNotAllowCreator) do
27
- def i_should_be_a_scenario
28
- end
29
- end
30
- creator_subclass.instance_methods.should include('i_should_be_a_scenario')
31
-
32
- creator = creator_subclass.new(@space, @subject)
33
- creator.i_should_be_a_scenario.should be_instance_of(Scenario)
34
- end
35
- end
36
-
37
- describe DoNotAllowCreator, ".new with block" do
38
- it_should_behave_like "RR::DoNotAllowCreator"
39
-
40
- before do
41
- @creator = DoNotAllowCreator.new(@space, @subject) do |c|
42
- c.any_args
43
- c.no_args.with_no_args
44
- c.with_args(1, 2)
45
- end
46
- end
47
-
48
- it "raises TimesCalledError when any_args is called with no arguments" do
49
- proc {@subject.any_args}.should raise_error(Errors::TimesCalledError)
50
- end
51
-
52
- it "raises TimesCalledError when any_args is called with arguments" do
53
- proc {@subject.any_args(1, 2)}.should raise_error(Errors::TimesCalledError)
54
- end
55
-
56
- it "raises TimesCalledError when no_args is called with no arguments" do
57
- proc {@subject.no_args}.should raise_error(Errors::TimesCalledError)
58
- end
59
-
60
- it "does not raise TimesCalledError when no_args is called with arguments" do
61
- proc {@subject.no_args(1, 2)}.should raise_error(Errors::ScenarioNotFoundError)
62
- end
63
-
64
- it "raises TimesCalledError when any_args is called with no arguments" do
65
- proc {@subject.with_args}.should raise_error(Errors::ScenarioNotFoundError)
66
- end
67
-
68
- it "raises TimesCalledError when any_args is called with arguments" do
69
- proc {@subject.any_args(1, 2)}.should raise_error(Errors::TimesCalledError)
70
- end
71
-
72
- it "clears out all methods from creator" do
73
- creator_subclass = Class.new(DoNotAllowCreator) do
74
- def i_should_be_a_scenario
75
- end
76
- end
77
- creator_subclass.instance_methods.should include('i_should_be_a_scenario')
78
-
79
- creator = creator_subclass.new(@space, @subject)
80
- creator.i_should_be_a_scenario.should be_instance_of(Scenario)
81
- end
82
- end
83
-
84
- describe DoNotAllowCreator, "#method_missing" do
85
- it_should_behave_like "RR::DoNotAllowCreator"
86
-
87
- before do
88
- @subject = Object.new
89
- @creator = DoNotAllowCreator.new(@space, @subject)
90
- end
91
-
92
- it "sets expectation for method to never be called with any arguments when on arguments passed in" do
93
- @creator.foobar
94
- proc {@subject.foobar}.should raise_error(Errors::TimesCalledError)
95
- proc {@subject.foobar(1, 2)}.should raise_error(Errors::TimesCalledError)
96
- end
97
-
98
- it "sets expectation for method to never be called with passed in arguments" do
99
- @creator.foobar(1, 2)
100
- proc {@subject.foobar}.should raise_error(Errors::ScenarioNotFoundError)
101
- proc {@subject.foobar(1, 2)}.should raise_error(Errors::TimesCalledError)
102
- end
103
-
104
- it "sets expectation for method to never be called with no arguments when with_no_args is set" do
105
- @creator.foobar.with_no_args
106
- proc {@subject.foobar}.should raise_error(Errors::TimesCalledError)
107
- proc {@subject.foobar(1, 2)}.should raise_error(Errors::ScenarioNotFoundError)
108
- end
109
- end
110
-
111
- end