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
@@ -0,0 +1,8 @@
1
+ module RR
2
+ module Errors
3
+ module SpyVerificationErrors
4
+ class DoubleInjectionNotFoundError < SpyVerificationError
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module RR
2
+ module Errors
3
+ module SpyVerificationErrors
4
+ class InvocationCountError < SpyVerificationError
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module RR
2
+ module Errors
3
+ module SpyVerificationErrors
4
+ class SpyVerificationError < RRError
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ module RR
2
+ module Errors
3
+ class SubjectDoesNotImplementMethodError < RRError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module RR
2
+ module Errors
3
+ class SubjectHasDifferentArityError < RRError
4
+ end
5
+ end
6
+ end
@@ -2,28 +2,26 @@ module RR
2
2
  module Expectations
3
3
  class TimesCalledExpectation #:nodoc:
4
4
  attr_reader :double, :times_called
5
- attr_accessor :matcher
6
5
 
7
- def initialize(double, matcher=nil)
6
+ def initialize(double)
8
7
  @double = double
9
- @matcher = matcher
10
8
  @times_called = 0
11
9
  @verify_backtrace = caller[1..-1]
12
10
  end
13
11
 
14
12
  def attempt?
15
- @matcher.attempt?(@times_called)
13
+ times_matcher.attempt?(@times_called)
16
14
  end
17
15
 
18
16
  def attempt
19
17
  @times_called += 1
20
- verify_input_error unless @matcher.possible_match?(@times_called)
18
+ verify_input_error unless times_matcher.possible_match?(@times_called)
21
19
  return
22
20
  end
23
21
 
24
22
  def verify
25
- return false unless @matcher.is_a?(TimesCalledMatchers::TimesCalledMatcher)
26
- return @matcher.matches?(@times_called)
23
+ return false unless times_matcher.is_a?(TimesCalledMatchers::TimesCalledMatcher)
24
+ return times_matcher.matches?(@times_called)
27
25
  end
28
26
 
29
27
  def verify!
@@ -39,16 +37,20 @@ module RR
39
37
  end
40
38
 
41
39
  def terminal?
42
- @matcher.terminal?
40
+ times_matcher.terminal?
43
41
  end
44
42
 
45
43
  protected
44
+ def times_matcher
45
+ double.definition.times_matcher
46
+ end
47
+
46
48
  def verify_input_error
47
49
  raise Errors::TimesCalledError, error_message
48
50
  end
49
51
 
50
52
  def error_message
51
- "#{double.formatted_name}\n#{@matcher.error_message(@times_called)}"
53
+ "#{double.formatted_name}\n#{times_matcher.error_message(@times_called)}"
52
54
  end
53
55
  end
54
56
  end
@@ -0,0 +1,103 @@
1
+ module RR
2
+ class RecordedCalls
3
+ include RR::Space::Reader
4
+
5
+ def initialize(recorded_calls=[])
6
+ @recorded_calls = recorded_calls
7
+ @ordered_index = 0
8
+ end
9
+
10
+ attr_reader :recorded_calls
11
+
12
+ def clear
13
+ self.ordered_index = 0
14
+ recorded_calls.clear
15
+ end
16
+
17
+ def <<(recorded_call)
18
+ recorded_calls << recorded_call
19
+ end
20
+
21
+ def any?(&block)
22
+ recorded_calls.any?(&block)
23
+ end
24
+
25
+ def ==(other)
26
+ recorded_calls == other.recorded_calls
27
+ end
28
+
29
+ def match_error(spy_verification)
30
+ double_injection_exists_error(spy_verification) || begin
31
+ if spy_verification.ordered?
32
+ ordered_match_error(spy_verification)
33
+ else
34
+ unordered_match_error(spy_verification)
35
+ end
36
+ end
37
+ end
38
+
39
+ protected
40
+ attr_accessor :ordered_index
41
+
42
+ def double_injection_exists_error(spy_verification)
43
+ unless space.double_injection_exists?(spy_verification.subject, spy_verification.method_name)
44
+ RR::Errors::SpyVerificationErrors::DoubleInjectionNotFoundError.new(
45
+ "A Double Injection for the subject and method call:\n" <<
46
+ "#{spy_verification.subject.inspect}\n" <<
47
+ "#{spy_verification.method_name}\ndoes not exist in:\n" <<
48
+ "\t#{recorded_calls.map {|call| call.inspect}.join("\n\t")}"
49
+ )
50
+ end
51
+ end
52
+
53
+ def ordered_match_error(spy_verification)
54
+ memoized_matching_recorded_calls = matching_recorded_calls(spy_verification)
55
+
56
+ if memoized_matching_recorded_calls.last
57
+ self.ordered_index = recorded_calls.index(memoized_matching_recorded_calls.last)
58
+ end
59
+ (0..memoized_matching_recorded_calls.size).to_a.any? do |i|
60
+ spy_verification.times_matcher.matches?(i)
61
+ end ? nil : invocation_count_error(spy_verification, memoized_matching_recorded_calls)
62
+ end
63
+
64
+ def unordered_match_error(spy_verification)
65
+ memoized_matching_recorded_calls = matching_recorded_calls(spy_verification)
66
+
67
+ spy_verification.times_matcher.matches?(
68
+ memoized_matching_recorded_calls.size
69
+ ) ? nil : invocation_count_error(spy_verification, memoized_matching_recorded_calls)
70
+ end
71
+
72
+ def matching_recorded_calls(spy_verification)
73
+ recorded_calls[ordered_index..-1].
74
+ select(&match_double_injection(spy_verification)).
75
+ select(&match_argument_expectation(spy_verification))
76
+ end
77
+
78
+ def match_double_injection(spy_verification)
79
+ lambda do |recorded_call|
80
+ recorded_call[0] == spy_verification.subject &&
81
+ recorded_call[1] == spy_verification.method_name
82
+ end
83
+ end
84
+
85
+ def match_argument_expectation(spy_verification)
86
+ lambda do |recorded_call|
87
+ spy_verification.argument_expectation.exact_match?(*recorded_call[2]) ||
88
+ spy_verification.argument_expectation.wildcard_match?(*recorded_call[2])
89
+ end
90
+ end
91
+
92
+ def invocation_count_error(spy_verification, matching_recorded_calls)
93
+ RR::Errors::SpyVerificationErrors::InvocationCountError.new(
94
+ "On subject #{spy_verification.subject.inspect}\n" <<
95
+ "Expected #{Double.formatted_name(spy_verification.method_name, spy_verification.argument_expectation.expected_arguments)}\n" <<
96
+ "to be called #{spy_verification.times_matcher.expected_times_message},\n" <<
97
+ "but was called #{matching_recorded_calls.size} times.\n" <<
98
+ "All of the method calls related to Doubles are:\n" <<
99
+ "\t#{recorded_calls.map {|call| call.inspect}.join("\n\t")}"
100
+ )
101
+ end
102
+ end
103
+ end
data/lib/rr/space.rb CHANGED
@@ -19,12 +19,13 @@ module RR
19
19
  end
20
20
  end
21
21
 
22
- attr_reader :double_injections, :ordered_doubles
22
+ attr_reader :double_injections, :ordered_doubles, :recorded_calls
23
23
  attr_accessor :trim_backtrace
24
24
  def initialize
25
25
  @double_injections = HashWithObjectIdKey.new
26
26
  @ordered_doubles = []
27
27
  @trim_backtrace = false
28
+ @recorded_calls = RR::RecordedCalls.new
28
29
  end
29
30
 
30
31
  # Reuses or creates, if none exists, a DoubleInjection for the passed
@@ -32,13 +33,13 @@ module RR
32
33
  # When a DoubleInjection is created, it binds the dispatcher to the
33
34
  # subject.
34
35
  def double_injection(subject, method_name)
35
- double_injection = @double_injections[subject][method_name.to_sym]
36
- return double_injection if double_injection
36
+ @double_injections[subject][method_name.to_sym] ||= begin
37
+ DoubleInjection.new(subject, method_name.to_sym).bind
38
+ end
39
+ end
37
40
 
38
- double_injection = DoubleInjection.new(subject, method_name.to_sym)
39
- @double_injections[subject][method_name.to_sym] = double_injection
40
- double_injection.bind
41
- double_injection
41
+ def double_injection_exists?(subject, method_name)
42
+ !!@double_injections[subject][method_name.to_sym]
42
43
  end
43
44
 
44
45
  # Registers the ordered Double to be verified.
@@ -79,6 +80,7 @@ module RR
79
80
  def reset
80
81
  reset_ordered_doubles
81
82
  reset_double_injections
83
+ reset_recorded_calls
82
84
  end
83
85
 
84
86
  # Verifies the DoubleInjection for the passed in subject and method_name.
@@ -94,6 +96,10 @@ module RR
94
96
  @double_injections.delete(subject) if @double_injections[subject].empty?
95
97
  double_injection.reset
96
98
  end
99
+
100
+ def record_call(subject, method_name, arguments, block)
101
+ @recorded_calls << [subject, method_name, arguments, block]
102
+ end
97
103
 
98
104
  protected
99
105
  # Removes the ordered Doubles from the list
@@ -108,6 +114,10 @@ module RR
108
114
  reset_double(subject, method_name)
109
115
  end
110
116
  end
111
- end
117
+ end
118
+
119
+ def reset_recorded_calls
120
+ @recorded_calls.clear
121
+ end
112
122
  end
113
123
  end
@@ -0,0 +1,48 @@
1
+ module RR
2
+ class SpyVerification
3
+ def initialize(subject, method_name, args)
4
+ @subject = subject
5
+ @method_name = method_name.to_sym
6
+ set_argument_expectation_for_args(args)
7
+ @ordered = false
8
+ once
9
+ end
10
+
11
+ attr_reader :argument_expectation, :method_name, :times_matcher
12
+ attr_accessor :subject
13
+
14
+ include RR::DoubleDefinitions::DoubleDefinition::TimesDefinitionConstructionMethods
15
+ include RR::DoubleDefinitions::DoubleDefinition::ArgumentDefinitionConstructionMethods
16
+
17
+ def ordered
18
+ @ordered = true
19
+ self
20
+ end
21
+
22
+ def ordered?
23
+ @ordered
24
+ end
25
+
26
+ def call
27
+ (error = RR.recorded_calls.match_error(self)) && raise(error)
28
+ end
29
+
30
+ def to_proc
31
+ lambda do
32
+ call
33
+ end
34
+ end
35
+
36
+ protected
37
+ attr_writer :times_matcher
38
+
39
+ def set_argument_expectation_for_args(args)
40
+ # with_no_args and with actually set @argument_expectation
41
+ args.empty? ? with_no_args : with(*args)
42
+ end
43
+
44
+ def install_method_callback(return_value_block)
45
+ # Do nothing. This is to support DefinitionConstructionMethods
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ module RR
2
+ class SpyVerificationProxy
3
+ instance_methods.each do |m|
4
+ unless m =~ /^_/ || m.to_s == 'object_id' || m.to_s == "instance_eval" || m.to_s == 'respond_to?'
5
+ alias_method "__blank_slated_#{m}", m
6
+ undef_method m
7
+ end
8
+ end
9
+
10
+ def initialize(subject)
11
+ @subject = subject
12
+ end
13
+
14
+ def method_missing(method_name, *args, &block)
15
+ SpyVerification.new(@subject, method_name, args)
16
+ end
17
+ end
18
+ end
@@ -10,9 +10,8 @@ module RR
10
10
  true
11
11
  end
12
12
 
13
- protected
14
- def expected_message_part
15
- "Expected any number of times."
13
+ def expected_times_message
14
+ "any number of times"
16
15
  end
17
16
  end
18
17
  end
@@ -7,9 +7,8 @@ module RR
7
7
  times_called >= @times
8
8
  end
9
9
 
10
- protected
11
- def expected_message_part
12
- "Expected at least #{@times.inspect} times."
10
+ def expected_times_message
11
+ "at least #{@times.inspect} times"
13
12
  end
14
13
  end
15
14
  end
@@ -15,9 +15,8 @@ module RR
15
15
  times_called < @times
16
16
  end
17
17
 
18
- protected
19
- def expected_message_part
20
- "Expected at most #{@times.inspect} times."
18
+ def expected_times_message
19
+ "at most #{@times.inspect} times"
21
20
  end
22
21
  end
23
22
  end
@@ -24,18 +24,18 @@ module RR
24
24
  end
25
25
 
26
26
  def error_message(times_called)
27
- "Called #{times_called.inspect} #{pluralized_time(times_called)}.\n#{expected_message_part}"
27
+ "Called #{times_called.inspect} #{pluralized_time(times_called)}.\nExpected #{expected_times_message}."
28
28
  end
29
29
 
30
30
  def ==(other)
31
31
  self.class == other.class && self.times == other.times
32
32
  end
33
33
 
34
- protected
35
- def expected_message_part
36
- "Expected #{@times.inspect} times."
34
+ def expected_times_message
35
+ "#{@times.inspect} times"
37
36
  end
38
37
 
38
+ protected
39
39
  def pluralized_time(times_called)
40
40
  (times_called == 1) ? "time" : "times"
41
41
  end
@@ -9,6 +9,7 @@ class CoreExampleSuite
9
9
  puts "Running Rspec Example Suite"
10
10
  files.each do |file|
11
11
  require file
12
+ # puts "require '#{file}'"
12
13
  end
13
14
  end
14
15
  end
@@ -14,7 +14,7 @@ describe "RR" do
14
14
  RR.reset
15
15
  end
16
16
 
17
- describe "RR mock:" do
17
+ describe "mock" do
18
18
  it "mocks via inline call" do
19
19
  mock(subject).to_s {"a value"}
20
20
  subject.to_s.should == "a value"
@@ -29,14 +29,20 @@ describe "RR" do
29
29
  subject.to_s.should == "value 2"
30
30
  lambda {subject.to_s}.should raise_error(RR::Errors::TimesCalledError)
31
31
  end
32
-
32
+
33
33
  it 'allows terse chaining' do
34
34
  mock(subject).first(1) {mock(Object.new).second(2) {mock(Object.new).third(3) {4}}}
35
35
  subject.first(1).second(2).third(3).should == 4
36
36
 
37
37
  mock(subject).first(1) {mock!.second(2) {mock!.third(3) {4}}}
38
38
  subject.first(1).second(2).third(3).should == 4
39
-
39
+
40
+ mock(subject).first(1) {mock!.second(2).mock!.third(3) {4}}
41
+ subject.first(1).second(2).third(3).should == 4
42
+
43
+ mock(subject).first(1) {mock!.second(2).mock! {third(3) {4}}}
44
+ subject.first(1).second(2).third(3).should == 4
45
+
40
46
  mock(subject).first(1).mock!.second(2).mock!.third(3) {4}
41
47
  subject.first(1).second(2).third(3).should == 4
42
48
  end
@@ -55,7 +61,7 @@ describe "RR" do
55
61
  mock.proxy(subject).find('1').mock.proxy!.child
56
62
  subject.find('1').child.should == :the_child
57
63
  end
58
-
64
+
59
65
  it 'allows branched chaining' do
60
66
  mock(subject).first do
61
67
  mock! do |expect|
@@ -67,7 +73,7 @@ describe "RR" do
67
73
  o.branch1.branch11.should == 11
68
74
  o.branch2.branch22.should == 22
69
75
  end
70
-
76
+
71
77
  it 'allows chained ordering' do
72
78
  mock(subject).to_s {"value 1"}.then.to_s {"value 2"}.twice.then.to_s {"value 3"}.once
73
79
  subject.to_s.should == "value 1"
@@ -77,7 +83,7 @@ describe "RR" do
77
83
  lambda {subject.to_s}.should raise_error(RR::Errors::TimesCalledError)
78
84
  end
79
85
 
80
- it "mocks via block" do
86
+ it "mocks via block with argument" do
81
87
  mock subject do |c|
82
88
  c.to_s {"a value"}
83
89
  c.to_sym {:crazy}
@@ -86,6 +92,15 @@ describe "RR" do
86
92
  subject.to_sym.should == :crazy
87
93
  end
88
94
 
95
+ it "mocks via block without argument" do
96
+ mock subject do
97
+ to_s {"a value"}
98
+ to_sym {:crazy}
99
+ end
100
+ subject.to_s.should == "a value"
101
+ subject.to_sym.should == :crazy
102
+ end
103
+
89
104
  it "has wildcard matchers" do
90
105
  mock(subject).foobar(
91
106
  is_a(String),
@@ -118,7 +133,7 @@ describe "RR" do
118
133
  end
119
134
  end
120
135
 
121
- describe "RR proxy:" do
136
+ describe "proxy" do
122
137
  it "proxies via inline call" do
123
138
  expected_to_s_value = subject.to_s
124
139
  mock.proxy(subject).to_s
@@ -152,7 +167,7 @@ describe "RR" do
152
167
  lambda {subject.to_s(:bar)}.should raise_error(RR::Errors::TimesCalledError)
153
168
  end
154
169
 
155
- it "proxies via block" do
170
+ it "proxies via block with argument" do
156
171
  def subject.foobar_1(*args)
157
172
  :original_value_1
158
173
  end
@@ -172,7 +187,7 @@ describe "RR" do
172
187
  lambda {subject.foobar_2(:blah)}.should raise_error
173
188
  end
174
189
 
175
- it "proxies via block" do
190
+ it "proxies via block without argument" do
176
191
  def subject.foobar_1(*args)
177
192
  :original_value_1
178
193
  end
@@ -181,9 +196,9 @@ describe "RR" do
181
196
  :original_value_2
182
197
  end
183
198
 
184
- mock.proxy subject do |c|
185
- c.foobar_1(1)
186
- c.foobar_2
199
+ mock.proxy subject do
200
+ foobar_1(1)
201
+ foobar_2
187
202
  end
188
203
  subject.foobar_1(1).should == :original_value_1
189
204
  lambda {subject.foobar_1(:blah)}.should raise_error
@@ -193,7 +208,7 @@ describe "RR" do
193
208
  end
194
209
  end
195
210
 
196
- describe "RR stub:" do
211
+ describe "stub" do
197
212
  it "stubs via inline call" do
198
213
  stub(subject).to_s {"a value"}
199
214
  subject.to_s.should == "a value"
@@ -207,7 +222,7 @@ describe "RR" do
207
222
  subject.to_s.should == "value 2"
208
223
  end
209
224
 
210
- it "stubs via block" do
225
+ it "stubs via block with argument" do
211
226
  stub subject do |d|
212
227
  d.to_s {"a value"}
213
228
  d.to_sym {:crazy}
@@ -216,6 +231,15 @@ describe "RR" do
216
231
  subject.to_sym.should == :crazy
217
232
  end
218
233
 
234
+ it "stubs via block without argument" do
235
+ stub subject do
236
+ to_s {"a value"}
237
+ to_sym {:crazy}
238
+ end
239
+ subject.to_s.should == "a value"
240
+ subject.to_sym.should == :crazy
241
+ end
242
+
219
243
  it "stubs instance_of" do
220
244
  stub.instance_of(HighLevelSpec) do |o|
221
245
  o.to_s {"High Level Spec"}
@@ -228,4 +252,117 @@ describe "RR" do
228
252
  (subject == 55).should == :equality
229
253
  end
230
254
  end
255
+
256
+ describe "RR recorded_calls" do
257
+ it "should verify method calls after the fact" do
258
+ stub(subject).pig_rabbit
259
+ subject.pig_rabbit("bacon", "bunny meat")
260
+ #subject.should have_received.pig_rabitt("bacon", "bunny meat")
261
+ received(subject).pig_rabbit("bacon", "bunny meat").call
262
+ end
263
+
264
+ it "should verify method calls after the fact" do
265
+ stub(subject).pig_rabbit
266
+ lambda do
267
+ received(subject).pig_rabbit("bacon", "bunny meat").call
268
+ end.should raise_error(RR::Errors::SpyVerificationErrors::SpyVerificationError)
269
+ end
270
+ end
271
+
272
+ class StrongTestObject
273
+ def method_with_no_arguments
274
+ end
275
+
276
+ def method_with_one_argument(string)
277
+ end
278
+
279
+ def method_with_two_arguments(string, integer)
280
+ end
281
+
282
+ def method_with_three_arguments_including_varargs(string, integer, *args)
283
+ end
284
+
285
+ def method_with_varargs(*args)
286
+ end
287
+ end
288
+
289
+ describe "strong" do
290
+ context "when the method does not exist" do
291
+ it "raises an exception" do
292
+ lambda do
293
+ strong.stub(StrongTestObject.new).something
294
+ end.should raise_error(RR::Errors::SubjectDoesNotImplementMethodError)
295
+ end
296
+ end
297
+
298
+ context "when the method exists with no arguments" do
299
+ it "does not raise an exception" do
300
+ strong.stub(StrongTestObject.new).method_with_no_arguments
301
+ end
302
+ end
303
+
304
+ context "when the method has a different arity" do
305
+ it "raises an exception" do
306
+ lambda do
307
+ strong.stub(StrongTestObject.new).method_with_one_argument
308
+ end.should raise_error(RR::Errors::SubjectHasDifferentArityError)
309
+ end
310
+ end
311
+
312
+ context "when the method has accepts a variable number of arguments" do
313
+ it "does not raise an exception" do
314
+ strong.stub(StrongTestObject.new).method_with_varargs
315
+ end
316
+ end
317
+
318
+ context "when the method does not provide the required parameters before varargs" do
319
+ it "raises an exception" do
320
+ lambda do
321
+ strong.stub(StrongTestObject.new).method_with_three_arguments_including_varargs
322
+ end.should raise_error(RR::Errors::SubjectHasDifferentArityError)
323
+ end
324
+ end
325
+
326
+ context "when the minimum number of parameters are provided" do
327
+ it "does not raise an exception" do
328
+ strong.stub(StrongTestObject.new).method_with_three_arguments_including_varargs("one", 2)
329
+ end
330
+ end
331
+
332
+ context "when using instance_of and the method does not exist" do
333
+ it "raises an exception" do
334
+ lambda do
335
+ strong.stub.instance_of(StrongTestObject).something
336
+ StrongTestObject.new
337
+ end.should raise_error(RR::Errors::SubjectDoesNotImplementMethodError)
338
+ end
339
+ end
340
+
341
+ context "when using instance_of and the method does exist" do
342
+ it "does not raise an exception" do
343
+ strong.stub.instance_of(StrongTestObject).method_with_no_arguments
344
+ end
345
+ end
346
+ end
347
+
348
+ describe "spy" do
349
+ it "should record all method invocations" do
350
+ subject = Object.new
351
+ def subject.something
352
+ end
353
+
354
+ def subject.something_else
355
+ end
356
+
357
+ spy(subject)
358
+
359
+ subject.something
360
+ subject.something_else
361
+ subject.to_s
362
+
363
+ received(subject).something.call
364
+ received(subject).something_else.call
365
+ received(subject).to_s.call
366
+ end
367
+ end
231
368
  end