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