redinger-rr 0.10.3

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 (133) hide show
  1. data/CHANGES +221 -0
  2. data/README.rdoc +343 -0
  3. data/Rakefile +88 -0
  4. data/VERSION.yml +4 -0
  5. data/lib/rr.rb +88 -0
  6. data/lib/rr/adapters/rr_methods.rb +122 -0
  7. data/lib/rr/adapters/rspec.rb +59 -0
  8. data/lib/rr/adapters/test_unit.rb +29 -0
  9. data/lib/rr/double.rb +152 -0
  10. data/lib/rr/double_definitions/child_double_definition_creator.rb +27 -0
  11. data/lib/rr/double_definitions/double_definition.rb +348 -0
  12. data/lib/rr/double_definitions/double_definition_creator.rb +167 -0
  13. data/lib/rr/double_definitions/double_definition_creator_proxy.rb +37 -0
  14. data/lib/rr/double_definitions/strategies/implementation/implementation_strategy.rb +15 -0
  15. data/lib/rr/double_definitions/strategies/implementation/proxy.rb +62 -0
  16. data/lib/rr/double_definitions/strategies/implementation/reimplementation.rb +14 -0
  17. data/lib/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation.rb +17 -0
  18. data/lib/rr/double_definitions/strategies/scope/instance.rb +15 -0
  19. data/lib/rr/double_definitions/strategies/scope/instance_of_class.rb +50 -0
  20. data/lib/rr/double_definitions/strategies/scope/scope_strategy.rb +15 -0
  21. data/lib/rr/double_definitions/strategies/strategy.rb +70 -0
  22. data/lib/rr/double_definitions/strategies/verification/dont_allow.rb +34 -0
  23. data/lib/rr/double_definitions/strategies/verification/mock.rb +44 -0
  24. data/lib/rr/double_definitions/strategies/verification/stub.rb +45 -0
  25. data/lib/rr/double_definitions/strategies/verification/verification_strategy.rb +15 -0
  26. data/lib/rr/double_injection.rb +180 -0
  27. data/lib/rr/double_matches.rb +51 -0
  28. data/lib/rr/errors/argument_equality_error.rb +6 -0
  29. data/lib/rr/errors/double_definition_error.rb +6 -0
  30. data/lib/rr/errors/double_not_found_error.rb +6 -0
  31. data/lib/rr/errors/double_order_error.rb +6 -0
  32. data/lib/rr/errors/rr_error.rb +20 -0
  33. data/lib/rr/errors/spy_verification_errors/double_injection_not_found_error.rb +8 -0
  34. data/lib/rr/errors/spy_verification_errors/invocation_count_error.rb +8 -0
  35. data/lib/rr/errors/spy_verification_errors/spy_verification_error.rb +8 -0
  36. data/lib/rr/errors/subject_does_not_implement_method_error.rb +6 -0
  37. data/lib/rr/errors/subject_has_different_arity_error.rb +6 -0
  38. data/lib/rr/errors/times_called_error.rb +6 -0
  39. data/lib/rr/expectations/any_argument_expectation.rb +21 -0
  40. data/lib/rr/expectations/argument_equality_expectation.rb +41 -0
  41. data/lib/rr/expectations/times_called_expectation.rb +57 -0
  42. data/lib/rr/hash_with_object_id_key.rb +44 -0
  43. data/lib/rr/method_dispatches/base_method_dispatch.rb +108 -0
  44. data/lib/rr/method_dispatches/method_dispatch.rb +61 -0
  45. data/lib/rr/method_dispatches/method_missing_dispatch.rb +49 -0
  46. data/lib/rr/proc_from_block.rb +7 -0
  47. data/lib/rr/recorded_calls.rb +103 -0
  48. data/lib/rr/space.rb +123 -0
  49. data/lib/rr/spy_verification.rb +48 -0
  50. data/lib/rr/spy_verification_proxy.rb +18 -0
  51. data/lib/rr/times_called_matchers/any_times_matcher.rb +18 -0
  52. data/lib/rr/times_called_matchers/at_least_matcher.rb +15 -0
  53. data/lib/rr/times_called_matchers/at_most_matcher.rb +23 -0
  54. data/lib/rr/times_called_matchers/integer_matcher.rb +19 -0
  55. data/lib/rr/times_called_matchers/non_terminal.rb +27 -0
  56. data/lib/rr/times_called_matchers/proc_matcher.rb +11 -0
  57. data/lib/rr/times_called_matchers/range_matcher.rb +21 -0
  58. data/lib/rr/times_called_matchers/terminal.rb +20 -0
  59. data/lib/rr/times_called_matchers/times_called_matcher.rb +44 -0
  60. data/lib/rr/wildcard_matchers.rb +158 -0
  61. data/lib/rr/wildcard_matchers/anything.rb +18 -0
  62. data/lib/rr/wildcard_matchers/boolean.rb +23 -0
  63. data/lib/rr/wildcard_matchers/duck_type.rb +32 -0
  64. data/lib/rr/wildcard_matchers/hash_including.rb +29 -0
  65. data/lib/rr/wildcard_matchers/is_a.rb +25 -0
  66. data/lib/rr/wildcard_matchers/numeric.rb +13 -0
  67. data/lib/rr/wildcard_matchers/range.rb +7 -0
  68. data/lib/rr/wildcard_matchers/regexp.rb +7 -0
  69. data/lib/rr/wildcard_matchers/satisfy.rb +26 -0
  70. data/spec/core_spec_suite.rb +19 -0
  71. data/spec/environment_fixture_setup.rb +7 -0
  72. data/spec/high_level_spec.rb +398 -0
  73. data/spec/proc_from_block_spec.rb +14 -0
  74. data/spec/rr/adapters/rr_methods_argument_matcher_spec.rb +67 -0
  75. data/spec/rr/adapters/rr_methods_creator_spec.rb +149 -0
  76. data/spec/rr/adapters/rr_methods_space_spec.rb +115 -0
  77. data/spec/rr/adapters/rr_methods_spec_helper.rb +11 -0
  78. data/spec/rr/adapters/rr_methods_times_matcher_spec.rb +17 -0
  79. data/spec/rr/double_definitions/child_double_definition_creator_spec.rb +112 -0
  80. data/spec/rr/double_definitions/double_definition_creator_proxy_spec.rb +155 -0
  81. data/spec/rr/double_definitions/double_definition_creator_spec.rb +502 -0
  82. data/spec/rr/double_definitions/double_definition_spec.rb +1165 -0
  83. data/spec/rr/double_injection/double_injection_spec.rb +339 -0
  84. data/spec/rr/double_injection/double_injection_verify_spec.rb +29 -0
  85. data/spec/rr/double_spec.rb +352 -0
  86. data/spec/rr/errors/rr_error_spec.rb +67 -0
  87. data/spec/rr/expectations/any_argument_expectation_spec.rb +47 -0
  88. data/spec/rr/expectations/anything_argument_equality_expectation_spec.rb +14 -0
  89. data/spec/rr/expectations/argument_equality_expectation_spec.rb +135 -0
  90. data/spec/rr/expectations/boolean_argument_equality_expectation_spec.rb +34 -0
  91. data/spec/rr/expectations/hash_including_argument_equality_expectation_spec.rb +82 -0
  92. data/spec/rr/expectations/hash_including_spec.rb +17 -0
  93. data/spec/rr/expectations/satisfy_argument_equality_expectation_spec.rb +59 -0
  94. data/spec/rr/expectations/satisfy_spec.rb +14 -0
  95. data/spec/rr/expectations/times_called_expectation/times_called_expectation_any_times_spec.rb +46 -0
  96. data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_least_spec.rb +69 -0
  97. data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_most_spec.rb +71 -0
  98. data/spec/rr/expectations/times_called_expectation/times_called_expectation_helper.rb +23 -0
  99. data/spec/rr/expectations/times_called_expectation/times_called_expectation_integer_spec.rb +104 -0
  100. data/spec/rr/expectations/times_called_expectation/times_called_expectation_proc_spec.rb +81 -0
  101. data/spec/rr/expectations/times_called_expectation/times_called_expectation_range_spec.rb +83 -0
  102. data/spec/rr/expectations/times_called_expectation/times_called_expectation_spec.rb +38 -0
  103. data/spec/rr/rspec/invocation_matcher_spec.rb +279 -0
  104. data/spec/rr/rspec/rspec_adapter_spec.rb +66 -0
  105. data/spec/rr/rspec/rspec_backtrace_tweaking_spec.rb +31 -0
  106. data/spec/rr/rspec/rspec_backtrace_tweaking_spec_fixture.rb +11 -0
  107. data/spec/rr/rspec/rspec_usage_spec.rb +86 -0
  108. data/spec/rr/space/hash_with_object_id_key_spec.rb +88 -0
  109. data/spec/rr/space/space_spec.rb +550 -0
  110. data/spec/rr/test_unit/test_helper.rb +7 -0
  111. data/spec/rr/test_unit/test_unit_backtrace_test.rb +36 -0
  112. data/spec/rr/test_unit/test_unit_integration_test.rb +57 -0
  113. data/spec/rr/times_called_matchers/any_times_matcher_spec.rb +47 -0
  114. data/spec/rr/times_called_matchers/at_least_matcher_spec.rb +55 -0
  115. data/spec/rr/times_called_matchers/at_most_matcher_spec.rb +70 -0
  116. data/spec/rr/times_called_matchers/integer_matcher_spec.rb +70 -0
  117. data/spec/rr/times_called_matchers/proc_matcher_spec.rb +55 -0
  118. data/spec/rr/times_called_matchers/range_matcher_spec.rb +76 -0
  119. data/spec/rr/times_called_matchers/times_called_matcher_spec.rb +118 -0
  120. data/spec/rr/wildcard_matchers/anything_spec.rb +24 -0
  121. data/spec/rr/wildcard_matchers/boolean_spec.rb +36 -0
  122. data/spec/rr/wildcard_matchers/duck_type_spec.rb +52 -0
  123. data/spec/rr/wildcard_matchers/is_a_spec.rb +32 -0
  124. data/spec/rr/wildcard_matchers/numeric_spec.rb +32 -0
  125. data/spec/rr/wildcard_matchers/range_spec.rb +35 -0
  126. data/spec/rr/wildcard_matchers/regexp_spec.rb +43 -0
  127. data/spec/rr_spec.rb +28 -0
  128. data/spec/rspec_spec_suite.rb +17 -0
  129. data/spec/spec_helper.rb +109 -0
  130. data/spec/spec_suite.rb +31 -0
  131. data/spec/spy_verification_spec.rb +129 -0
  132. data/spec/test_unit_spec_suite.rb +21 -0
  133. metadata +193 -0
@@ -0,0 +1,180 @@
1
+ module RR
2
+ # RR::DoubleInjection is the binding of an subject and a method.
3
+ # A double_injection has 0 to many Double objects. Each Double
4
+ # has Argument Expectations and Times called Expectations.
5
+ class DoubleInjection
6
+ include Space::Reader
7
+
8
+ MethodArguments = Struct.new(:arguments, :block)
9
+ attr_reader :subject, :method_name, :doubles, :subject_class
10
+
11
+ def initialize(subject, method_name, subject_class)
12
+ @subject = subject
13
+ @subject_class = subject_class
14
+ @method_name = method_name.to_sym
15
+ @doubles = []
16
+ end
17
+
18
+ # RR::DoubleInjection#register_double adds the passed in Double
19
+ # into this DoubleInjection's list of Double objects.
20
+ def register_double(double)
21
+ @doubles << double
22
+ end
23
+
24
+ # RR::DoubleInjection#bind injects a method that acts as a dispatcher
25
+ # that dispatches to the matching Double when the method
26
+ # is called.
27
+ def bind
28
+ if subject_respond_to_method?(method_name)
29
+ if subject_has_method_defined?(method_name)
30
+ bind_method_with_alias
31
+ else
32
+ me = self
33
+
34
+ subject_class.__send__(:alias_method, original_method_missing_alias_name, :method_missing)
35
+ bind_method_missing
36
+
37
+ subject_class.__send__(:alias_method, original_singleton_method_added_alias_name, :singleton_method_added)
38
+ subject_class.__send__(:define_method, :singleton_method_added) do |method_name_arg|
39
+ if method_name_arg.to_sym == me.method_name.to_sym
40
+ me.send(:deferred_bind_method)
41
+ end
42
+ send(me.send(:original_singleton_method_added_alias_name), method_name_arg)
43
+ end
44
+ @deferred_bind = true
45
+ end
46
+ else
47
+ bind_method
48
+ end
49
+ self
50
+ end
51
+
52
+ # RR::DoubleInjection#verify verifies each Double
53
+ # TimesCalledExpectation are met.
54
+ def verify
55
+ @doubles.each do |double|
56
+ double.verify
57
+ end
58
+ end
59
+
60
+ # RR::DoubleInjection#reset removes the injected dispatcher method.
61
+ # It binds the original method implementation on the subject
62
+ # if one exists.
63
+ def reset
64
+ reset_bound_method
65
+ reset_method_missing
66
+ reset_singleton_method_added
67
+ end
68
+
69
+ def reset_bound_method
70
+ if subject_has_original_method?
71
+ subject_class.__send__(:alias_method, method_name, original_method_alias_name)
72
+ subject_class.__send__(:remove_method, original_method_alias_name)
73
+ else
74
+ if subject_has_method_defined?(method_name)
75
+ subject_class.__send__(:remove_method, method_name)
76
+ end
77
+ end
78
+ end
79
+
80
+ def reset_method_missing
81
+ if subject_has_method_defined?(original_method_missing_alias_name)
82
+ me = self
83
+ subject_class.class_eval do
84
+ alias_method :method_missing, me.send(:original_method_missing_alias_name)
85
+ remove_method me.send(:original_method_missing_alias_name)
86
+ end
87
+ end
88
+ end
89
+
90
+ def reset_singleton_method_added
91
+ if @deferred_bind
92
+ me = self
93
+ subject_class.class_eval do
94
+ alias_method :singleton_method_added, me.send(:original_singleton_method_added_alias_name)
95
+ remove_method me.send(:original_singleton_method_added_alias_name)
96
+ end
97
+ end
98
+ end
99
+
100
+ def dispatch_method(args, block)
101
+ dispatch = MethodDispatches::MethodDispatch.new(self, args, block)
102
+ if @bypass_bound_method
103
+ dispatch.call_original_method
104
+ else
105
+ dispatch.call
106
+ end
107
+ end
108
+
109
+ def dispatch_method_missing(method_name, args, block)
110
+ MethodDispatches::MethodMissingDispatch.new(self, method_name, args, block).call
111
+ end
112
+
113
+ def subject_has_original_method?
114
+ subject_respond_to_method?(original_method_alias_name)
115
+ end
116
+
117
+ def subject_has_original_method_missing?
118
+ subject_respond_to_method?(original_method_missing_alias_name)
119
+ end
120
+
121
+ def original_method_alias_name
122
+ "__rr__original_#{@method_name}"
123
+ end
124
+
125
+ def original_method_missing_alias_name
126
+ "__rr__original_method_missing"
127
+ end
128
+
129
+ def subject_has_method_defined?(method_name)
130
+ @subject.methods.include?(method_name.to_s) || @subject.protected_methods.include?(method_name.to_s) || @subject.private_methods.include?(method_name.to_s)
131
+ end
132
+
133
+ def bypass_bound_method
134
+ @bypass_bound_method = true
135
+ yield
136
+ ensure
137
+ @bypass_bound_method = nil
138
+ end
139
+
140
+ protected
141
+ def deferred_bind_method
142
+ unless subject_has_method_defined?(original_method_alias_name)
143
+ bind_method_with_alias
144
+ end
145
+ @performed_deferred_bind = true
146
+ end
147
+
148
+ def bind_method_with_alias
149
+ subject_class.__send__(:alias_method, original_method_alias_name, method_name)
150
+ bind_method
151
+ end
152
+
153
+ def bind_method
154
+ returns_method = <<-METHOD
155
+ def #{@method_name}(*args, &block)
156
+ arguments = MethodArguments.new(args, block)
157
+ RR::Space.double_injection(self, :#{@method_name}).dispatch_method(arguments.arguments, arguments.block)
158
+ end
159
+ METHOD
160
+ subject_class.class_eval(returns_method, __FILE__, __LINE__ - 5)
161
+ end
162
+
163
+ def bind_method_missing
164
+ returns_method = <<-METHOD
165
+ def method_missing(method_name, *args, &block)
166
+ RR::Space.double_injection(self, :#{@method_name}).dispatch_method_missing(method_name, args, block)
167
+ end
168
+ METHOD
169
+ subject_class.class_eval(returns_method, __FILE__, __LINE__ - 4)
170
+ end
171
+
172
+ def original_singleton_method_added_alias_name
173
+ "__rr__original_singleton_method_added"
174
+ end
175
+
176
+ def subject_respond_to_method?(method_name)
177
+ subject_has_method_defined?(method_name) || @subject.respond_to?(method_name)
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,51 @@
1
+ module RR
2
+ class DoubleMatches
3
+ attr_reader :matching_doubles,
4
+ :exact_terminal_doubles_to_attempt,
5
+ :exact_non_terminal_doubles_to_attempt,
6
+ :wildcard_terminal_doubles_to_attempt,
7
+ :wildcard_non_terminal_doubles_to_attempt
8
+ def initialize(doubles) #:nodoc:
9
+ @doubles = doubles
10
+ @matching_doubles = []
11
+ @exact_terminal_doubles_to_attempt = []
12
+ @exact_non_terminal_doubles_to_attempt = []
13
+ @wildcard_terminal_doubles_to_attempt = []
14
+ @wildcard_non_terminal_doubles_to_attempt = []
15
+ end
16
+
17
+ def find_all_matches(args)
18
+ @doubles.each do |double|
19
+ if double.exact_match?(*args)
20
+ matching_doubles << double
21
+ if double.attempt?
22
+ exact_double_is_terminal_or_non_terminal double
23
+ end
24
+ elsif double.wildcard_match?(*args)
25
+ matching_doubles << double
26
+ if double.attempt?
27
+ wildcard_double_is_terminal_or_non_terminal double
28
+ end
29
+ end
30
+ end
31
+ self
32
+ end
33
+
34
+ protected
35
+ def exact_double_is_terminal_or_non_terminal(double)
36
+ if double.terminal?
37
+ exact_terminal_doubles_to_attempt << double
38
+ else
39
+ exact_non_terminal_doubles_to_attempt << double
40
+ end
41
+ end
42
+
43
+ def wildcard_double_is_terminal_or_non_terminal(double)
44
+ if double.terminal?
45
+ wildcard_terminal_doubles_to_attempt << double
46
+ else
47
+ wildcard_non_terminal_doubles_to_attempt << double
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,6 @@
1
+ module RR
2
+ module Errors
3
+ class ArgumentEqualityError < RRError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module RR
2
+ module Errors
3
+ class DoubleDefinitionError < RRError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module RR
2
+ module Errors
3
+ class DoubleNotFoundError < RRError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module RR
2
+ module Errors
3
+ class DoubleOrderError < RRError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,20 @@
1
+ module RR
2
+ module Errors
3
+ BACKTRACE_IDENTIFIER = Regexp.new("/lib/rr")
4
+
5
+ class RRError < RuntimeError
6
+ attr_writer :backtrace
7
+ def backtrace
8
+ original_backtrace = (@backtrace) ? @backtrace : super
9
+ return original_backtrace unless RR.trim_backtrace
10
+
11
+ return original_backtrace unless original_backtrace.respond_to?(:each)
12
+ new_backtrace = []
13
+ original_backtrace.each do |line|
14
+ new_backtrace << line unless line =~ BACKTRACE_IDENTIFIER
15
+ end
16
+ new_backtrace
17
+ end
18
+ end
19
+ end
20
+ end
@@ -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
@@ -0,0 +1,6 @@
1
+ module RR
2
+ module Errors
3
+ class TimesCalledError < RRError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ module RR
2
+ module Expectations
3
+ class AnyArgumentExpectation < ArgumentEqualityExpectation #:nodoc:
4
+ def initialize
5
+ super
6
+ end
7
+
8
+ def exact_match?(*arguments)
9
+ false
10
+ end
11
+
12
+ def wildcard_match?(*arguments)
13
+ true
14
+ end
15
+
16
+ def ==(other)
17
+ other.is_a?(self.class)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ module RR
2
+ module Expectations
3
+ class ArgumentEqualityExpectation #:nodoc:
4
+ attr_reader :expected_arguments
5
+
6
+ def initialize(*expected_arguments)
7
+ @expected_arguments = expected_arguments
8
+ end
9
+
10
+ def exact_match?(*arguments)
11
+ return false unless arguments.length == expected_arguments.length
12
+ arguments.each_with_index do |arg, index|
13
+ return false unless equality_match(expected_arguments[index], arg)
14
+ end
15
+ true
16
+ end
17
+
18
+ def wildcard_match?(*arguments)
19
+ return false unless arguments.length == expected_arguments.length
20
+ arguments.each_with_index do |arg, index|
21
+ expected_argument = expected_arguments[index]
22
+ if expected_argument.respond_to?(:wildcard_match?)
23
+ return false unless expected_argument.wildcard_match?(arg)
24
+ else
25
+ return false unless equality_match(expected_argument, arg)
26
+ end
27
+ end
28
+ true
29
+ end
30
+
31
+ def ==(other)
32
+ expected_arguments == other.expected_arguments
33
+ end
34
+
35
+ protected
36
+ def equality_match(arg1, arg2)
37
+ arg1.respond_to?(:'__rr__original_==') ? arg1.__send__(:'__rr__original_==', arg2) : arg1 == arg2
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,57 @@
1
+ module RR
2
+ module Expectations
3
+ class TimesCalledExpectation #:nodoc:
4
+ attr_reader :double, :times_called
5
+
6
+ def initialize(double)
7
+ @double = double
8
+ @times_called = 0
9
+ @verify_backtrace = caller[1..-1]
10
+ end
11
+
12
+ def attempt?
13
+ times_matcher.attempt?(@times_called)
14
+ end
15
+
16
+ def attempt
17
+ @times_called += 1
18
+ verify_input_error unless times_matcher.possible_match?(@times_called)
19
+ return
20
+ end
21
+
22
+ def verify
23
+ return false unless times_matcher.is_a?(TimesCalledMatchers::TimesCalledMatcher)
24
+ return times_matcher.matches?(@times_called)
25
+ end
26
+
27
+ def verify!
28
+ unless verify
29
+ if @verify_backtrace
30
+ error = Errors::TimesCalledError.new(error_message)
31
+ error.backtrace = @verify_backtrace
32
+ raise error
33
+ else
34
+ raise Errors::TimesCalledError, error_message
35
+ end
36
+ end
37
+ end
38
+
39
+ def terminal?
40
+ times_matcher.terminal?
41
+ end
42
+
43
+ protected
44
+ def times_matcher
45
+ double.definition.times_matcher
46
+ end
47
+
48
+ def verify_input_error
49
+ raise Errors::TimesCalledError, error_message
50
+ end
51
+
52
+ def error_message
53
+ "#{double.formatted_name}\n#{times_matcher.error_message(@times_called)}"
54
+ end
55
+ end
56
+ end
57
+ end