mocha 1.1.0 → 2.1.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 (266) hide show
  1. checksums.yaml +5 -5
  2. data/.github/FUNDING.yml +1 -0
  3. data/.rubocop.yml +53 -0
  4. data/.rubocop_todo.yml +27 -0
  5. data/.yardopts +1 -1
  6. data/CONTRIBUTING.md +4 -9
  7. data/COPYING.md +2 -2
  8. data/Gemfile +27 -0
  9. data/MIT-LICENSE.md +1 -1
  10. data/README.md +120 -30
  11. data/RELEASE.md +393 -1
  12. data/Rakefile +61 -50
  13. data/gemfiles/Gemfile.minitest.latest +1 -0
  14. data/gemfiles/Gemfile.test-unit.latest +1 -0
  15. data/lib/mocha/any_instance_method.rb +9 -74
  16. data/lib/mocha/api.rb +85 -69
  17. data/lib/mocha/argument_iterator.rb +4 -8
  18. data/lib/mocha/backtrace_filter.rb +3 -7
  19. data/lib/mocha/block_matcher.rb +31 -0
  20. data/lib/mocha/cardinality.rb +60 -49
  21. data/lib/mocha/central.rb +21 -12
  22. data/lib/mocha/change_state_side_effect.rb +0 -4
  23. data/lib/mocha/class_methods.rb +19 -20
  24. data/lib/mocha/configuration.rb +317 -52
  25. data/lib/mocha/debug.rb +2 -4
  26. data/lib/mocha/deprecation.rb +8 -11
  27. data/lib/mocha/detection/mini_test.rb +0 -2
  28. data/lib/mocha/detection/test_unit.rb +3 -5
  29. data/lib/mocha/error_with_filtered_backtrace.rb +13 -0
  30. data/lib/mocha/exception_raiser.rb +4 -6
  31. data/lib/mocha/expectation.rb +186 -95
  32. data/lib/mocha/expectation_error.rb +1 -1
  33. data/lib/mocha/expectation_error_factory.rb +0 -1
  34. data/lib/mocha/expectation_list.rb +7 -11
  35. data/lib/mocha/hooks.rb +1 -3
  36. data/lib/mocha/in_state_ordering_constraint.rb +0 -4
  37. data/lib/mocha/inspect.rb +30 -38
  38. data/lib/mocha/instance_method.rb +11 -8
  39. data/lib/mocha/integration/mini_test/adapter.rb +3 -5
  40. data/lib/mocha/integration/mini_test/exception_translation.rb +1 -1
  41. data/lib/mocha/integration/mini_test.rb +10 -31
  42. data/lib/mocha/integration/monkey_patcher.rb +8 -2
  43. data/lib/mocha/integration/test_unit/adapter.rb +8 -9
  44. data/lib/mocha/integration/test_unit.rb +10 -26
  45. data/lib/mocha/invocation.rb +73 -0
  46. data/lib/mocha/is_a.rb +0 -2
  47. data/lib/mocha/logger.rb +0 -4
  48. data/lib/mocha/macos_version.rb +5 -0
  49. data/lib/mocha/method_matcher.rb +1 -5
  50. data/lib/mocha/minitest.rb +6 -0
  51. data/lib/mocha/mock.rb +105 -57
  52. data/lib/mocha/mockery.rb +70 -98
  53. data/lib/mocha/names.rb +2 -12
  54. data/lib/mocha/not_initialized_error.rb +7 -0
  55. data/lib/mocha/object_methods.rb +25 -31
  56. data/lib/mocha/parameter_matchers/all_of.rb +2 -8
  57. data/lib/mocha/parameter_matchers/any_of.rb +2 -8
  58. data/lib/mocha/parameter_matchers/any_parameters.rb +3 -9
  59. data/lib/mocha/parameter_matchers/anything.rb +2 -8
  60. data/lib/mocha/parameter_matchers/base.rb +7 -13
  61. data/lib/mocha/parameter_matchers/equals.rb +1 -7
  62. data/lib/mocha/parameter_matchers/{query_string.rb → equivalent_uri.rb} +14 -15
  63. data/lib/mocha/parameter_matchers/has_entries.rb +2 -7
  64. data/lib/mocha/parameter_matchers/has_entry.rb +26 -21
  65. data/lib/mocha/parameter_matchers/has_key.rb +2 -7
  66. data/lib/mocha/parameter_matchers/has_keys.rb +53 -0
  67. data/lib/mocha/parameter_matchers/has_value.rb +2 -7
  68. data/lib/mocha/parameter_matchers/includes.rb +50 -8
  69. data/lib/mocha/parameter_matchers/instance_methods.rb +27 -0
  70. data/lib/mocha/parameter_matchers/instance_of.rb +0 -6
  71. data/lib/mocha/parameter_matchers/is_a.rb +2 -7
  72. data/lib/mocha/parameter_matchers/kind_of.rb +2 -6
  73. data/lib/mocha/parameter_matchers/not.rb +2 -7
  74. data/lib/mocha/parameter_matchers/optionally.rb +4 -10
  75. data/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb +64 -0
  76. data/lib/mocha/parameter_matchers/regexp_matches.rb +0 -6
  77. data/lib/mocha/parameter_matchers/responds_with.rb +3 -8
  78. data/lib/mocha/parameter_matchers/yaml_equivalent.rb +2 -6
  79. data/lib/mocha/parameter_matchers.rb +3 -4
  80. data/lib/mocha/parameters_matcher.rb +8 -11
  81. data/lib/mocha/raised_exception.rb +11 -0
  82. data/lib/mocha/receivers.rb +10 -14
  83. data/lib/mocha/return_values.rb +4 -8
  84. data/lib/mocha/ruby_version.rb +3 -0
  85. data/lib/mocha/sequence.rb +4 -9
  86. data/lib/mocha/single_return_value.rb +2 -5
  87. data/lib/mocha/state_machine.rb +33 -46
  88. data/lib/mocha/stubbed_method.rb +88 -0
  89. data/lib/mocha/stubbing_error.rb +2 -13
  90. data/lib/mocha/test_unit.rb +5 -2
  91. data/lib/mocha/thrower.rb +4 -6
  92. data/lib/mocha/thrown_object.rb +12 -0
  93. data/lib/mocha/version.rb +1 -1
  94. data/lib/mocha/yield_parameters.rb +7 -17
  95. data/mocha.gemspec +16 -43
  96. metadata +38 -248
  97. data/bin/build-matrix +0 -71
  98. data/gemfiles/Gemfile.minitest.1.3.0 +0 -7
  99. data/gemfiles/Gemfile.minitest.1.4.0 +0 -7
  100. data/gemfiles/Gemfile.minitest.1.4.1 +0 -7
  101. data/gemfiles/Gemfile.minitest.1.4.2 +0 -7
  102. data/gemfiles/Gemfile.minitest.2.0.0 +0 -7
  103. data/gemfiles/Gemfile.minitest.2.0.1 +0 -7
  104. data/gemfiles/Gemfile.minitest.2.11.0 +0 -7
  105. data/gemfiles/Gemfile.minitest.2.11.2 +0 -7
  106. data/gemfiles/Gemfile.minitest.2.3.0 +0 -7
  107. data/gemfiles/Gemfile.test-unit.2.0.0 +0 -7
  108. data/gemfiles/Gemfile.test-unit.2.0.1 +0 -7
  109. data/gemfiles/Gemfile.test-unit.2.0.3 +0 -7
  110. data/init.rb +0 -3
  111. data/lib/mocha/class_method.rb +0 -127
  112. data/lib/mocha/integration/mini_test/nothing.rb +0 -19
  113. data/lib/mocha/integration/mini_test/version_13.rb +0 -51
  114. data/lib/mocha/integration/mini_test/version_140.rb +0 -51
  115. data/lib/mocha/integration/mini_test/version_141.rb +0 -62
  116. data/lib/mocha/integration/mini_test/version_142_to_172.rb +0 -62
  117. data/lib/mocha/integration/mini_test/version_200.rb +0 -63
  118. data/lib/mocha/integration/mini_test/version_201_to_222.rb +0 -63
  119. data/lib/mocha/integration/mini_test/version_2110_to_2111.rb +0 -67
  120. data/lib/mocha/integration/mini_test/version_2112_to_320.rb +0 -70
  121. data/lib/mocha/integration/mini_test/version_230_to_2101.rb +0 -65
  122. data/lib/mocha/integration/test_unit/gem_version_200.rb +0 -59
  123. data/lib/mocha/integration/test_unit/gem_version_201_to_202.rb +0 -59
  124. data/lib/mocha/integration/test_unit/gem_version_203_to_220.rb +0 -59
  125. data/lib/mocha/integration/test_unit/gem_version_230_to_250.rb +0 -65
  126. data/lib/mocha/integration/test_unit/nothing.rb +0 -19
  127. data/lib/mocha/integration/test_unit/ruby_version_185_and_below.rb +0 -58
  128. data/lib/mocha/integration/test_unit/ruby_version_186_and_above.rb +0 -60
  129. data/lib/mocha/integration.rb +0 -14
  130. data/lib/mocha/mini_test.rb +0 -3
  131. data/lib/mocha/module_method.rb +0 -16
  132. data/lib/mocha/module_methods.rb +0 -14
  133. data/lib/mocha/multiple_yields.rb +0 -20
  134. data/lib/mocha/no_yields.rb +0 -11
  135. data/lib/mocha/parameter_matchers/object.rb +0 -17
  136. data/lib/mocha/pretty_parameters.rb +0 -28
  137. data/lib/mocha/setup.rb +0 -9
  138. data/lib/mocha/single_yield.rb +0 -18
  139. data/lib/mocha/standalone.rb +0 -4
  140. data/lib/mocha/unexpected_invocation.rb +0 -26
  141. data/lib/mocha_standalone.rb +0 -4
  142. data/test/acceptance/acceptance_test_helper.rb +0 -41
  143. data/test/acceptance/bug_18914_test.rb +0 -43
  144. data/test/acceptance/bug_21465_test.rb +0 -34
  145. data/test/acceptance/bug_21563_test.rb +0 -25
  146. data/test/acceptance/exception_rescue_test.rb +0 -55
  147. data/test/acceptance/expectations_on_multiple_methods_test.rb +0 -55
  148. data/test/acceptance/expected_invocation_count_test.rb +0 -232
  149. data/test/acceptance/failure_messages_test.rb +0 -64
  150. data/test/acceptance/issue_65_test.rb +0 -63
  151. data/test/acceptance/issue_70_test.rb +0 -55
  152. data/test/acceptance/mocha_example_test.rb +0 -98
  153. data/test/acceptance/mocha_test_result_test.rb +0 -84
  154. data/test/acceptance/mock_test.rb +0 -100
  155. data/test/acceptance/mock_with_initializer_block_test.rb +0 -51
  156. data/test/acceptance/mocked_methods_dispatch_test.rb +0 -78
  157. data/test/acceptance/multiple_expectations_failure_message_test.rb +0 -68
  158. data/test/acceptance/optional_parameters_test.rb +0 -70
  159. data/test/acceptance/parameter_matcher_test.rb +0 -337
  160. data/test/acceptance/partial_mocks_test.rb +0 -47
  161. data/test/acceptance/prepend_test.rb +0 -88
  162. data/test/acceptance/raise_exception_test.rb +0 -39
  163. data/test/acceptance/return_value_test.rb +0 -52
  164. data/test/acceptance/sequence_test.rb +0 -192
  165. data/test/acceptance/states_test.rb +0 -70
  166. data/test/acceptance/stub_any_instance_method_defined_on_superclass_test.rb +0 -34
  167. data/test/acceptance/stub_any_instance_method_test.rb +0 -238
  168. data/test/acceptance/stub_class_method_defined_on_active_record_association_proxy_test.rb +0 -106
  169. data/test/acceptance/stub_class_method_defined_on_class_test.rb +0 -78
  170. data/test/acceptance/stub_class_method_defined_on_module_test.rb +0 -75
  171. data/test/acceptance/stub_class_method_defined_on_superclass_test.rb +0 -112
  172. data/test/acceptance/stub_everything_test.rb +0 -56
  173. data/test/acceptance/stub_instance_method_defined_on_active_record_association_proxy_test.rb +0 -93
  174. data/test/acceptance/stub_instance_method_defined_on_class_and_aliased_test.rb +0 -69
  175. data/test/acceptance/stub_instance_method_defined_on_class_test.rb +0 -69
  176. data/test/acceptance/stub_instance_method_defined_on_kernel_module_test.rb +0 -75
  177. data/test/acceptance/stub_instance_method_defined_on_module_test.rb +0 -75
  178. data/test/acceptance/stub_instance_method_defined_on_object_class_test.rb +0 -75
  179. data/test/acceptance/stub_instance_method_defined_on_singleton_class_test.rb +0 -70
  180. data/test/acceptance/stub_instance_method_defined_on_superclass_test.rb +0 -72
  181. data/test/acceptance/stub_module_method_test.rb +0 -163
  182. data/test/acceptance/stub_test.rb +0 -52
  183. data/test/acceptance/stubba_example_test.rb +0 -102
  184. data/test/acceptance/stubba_test_result_test.rb +0 -66
  185. data/test/acceptance/stubbing_error_backtrace_test.rb +0 -64
  186. data/test/acceptance/stubbing_frozen_object_test.rb +0 -88
  187. data/test/acceptance/stubbing_method_accepting_block_parameter_test.rb +0 -48
  188. data/test/acceptance/stubbing_method_unnecessarily_test.rb +0 -65
  189. data/test/acceptance/stubbing_nil_test.rb +0 -59
  190. data/test/acceptance/stubbing_non_existent_any_instance_method_test.rb +0 -130
  191. data/test/acceptance/stubbing_non_existent_class_method_test.rb +0 -157
  192. data/test/acceptance/stubbing_non_existent_instance_method_test.rb +0 -147
  193. data/test/acceptance/stubbing_non_public_any_instance_method_test.rb +0 -130
  194. data/test/acceptance/stubbing_non_public_class_method_test.rb +0 -163
  195. data/test/acceptance/stubbing_non_public_instance_method_test.rb +0 -143
  196. data/test/acceptance/stubbing_on_non_mock_object_test.rb +0 -64
  197. data/test/acceptance/stubbing_same_class_method_on_parent_and_child_classes_test.rb +0 -35
  198. data/test/acceptance/throw_test.rb +0 -45
  199. data/test/acceptance/unexpected_invocation_test.rb +0 -25
  200. data/test/acceptance/unstubbing_test.rb +0 -168
  201. data/test/assertions.rb +0 -6
  202. data/test/deprecation_disabler.rb +0 -15
  203. data/test/execution_point.rb +0 -36
  204. data/test/integration/mini_test_test.rb +0 -8
  205. data/test/integration/shared_tests.rb +0 -174
  206. data/test/integration/test_unit_test.rb +0 -8
  207. data/test/method_definer.rb +0 -24
  208. data/test/mini_test_result.rb +0 -90
  209. data/test/minitest_result.rb +0 -49
  210. data/test/simple_counter.rb +0 -13
  211. data/test/test_helper.rb +0 -50
  212. data/test/test_runner.rb +0 -58
  213. data/test/test_unit_result.rb +0 -20
  214. data/test/unit/any_instance_method_test.rb +0 -132
  215. data/test/unit/array_inspect_test.rb +0 -16
  216. data/test/unit/backtrace_filter_test.rb +0 -19
  217. data/test/unit/cardinality_test.rb +0 -56
  218. data/test/unit/central_test.rb +0 -100
  219. data/test/unit/change_state_side_effect_test.rb +0 -41
  220. data/test/unit/class_method_test.rb +0 -223
  221. data/test/unit/class_methods_test.rb +0 -40
  222. data/test/unit/configuration_test.rb +0 -38
  223. data/test/unit/date_time_inspect_test.rb +0 -21
  224. data/test/unit/exception_raiser_test.rb +0 -42
  225. data/test/unit/expectation_list_test.rb +0 -82
  226. data/test/unit/expectation_test.rb +0 -497
  227. data/test/unit/hash_inspect_test.rb +0 -16
  228. data/test/unit/hooks_test.rb +0 -29
  229. data/test/unit/in_state_ordering_constraint_test.rb +0 -43
  230. data/test/unit/method_matcher_test.rb +0 -28
  231. data/test/unit/mock_test.rb +0 -341
  232. data/test/unit/mockery_test.rb +0 -151
  233. data/test/unit/module_methods_test.rb +0 -19
  234. data/test/unit/multiple_yields_test.rb +0 -18
  235. data/test/unit/no_yields_test.rb +0 -18
  236. data/test/unit/object_inspect_test.rb +0 -38
  237. data/test/unit/object_methods_test.rb +0 -46
  238. data/test/unit/parameter_matchers/all_of_test.rb +0 -26
  239. data/test/unit/parameter_matchers/any_of_test.rb +0 -26
  240. data/test/unit/parameter_matchers/anything_test.rb +0 -21
  241. data/test/unit/parameter_matchers/equals_test.rb +0 -25
  242. data/test/unit/parameter_matchers/has_entries_test.rb +0 -51
  243. data/test/unit/parameter_matchers/has_entry_test.rb +0 -129
  244. data/test/unit/parameter_matchers/has_key_test.rb +0 -55
  245. data/test/unit/parameter_matchers/has_value_test.rb +0 -57
  246. data/test/unit/parameter_matchers/includes_test.rb +0 -59
  247. data/test/unit/parameter_matchers/instance_of_test.rb +0 -25
  248. data/test/unit/parameter_matchers/is_a_test.rb +0 -25
  249. data/test/unit/parameter_matchers/kind_of_test.rb +0 -25
  250. data/test/unit/parameter_matchers/not_test.rb +0 -26
  251. data/test/unit/parameter_matchers/regexp_matches_test.rb +0 -46
  252. data/test/unit/parameter_matchers/responds_with_test.rb +0 -32
  253. data/test/unit/parameter_matchers/stub_matcher.rb +0 -27
  254. data/test/unit/parameter_matchers/yaml_equivalent_test.rb +0 -25
  255. data/test/unit/parameters_matcher_test.rb +0 -121
  256. data/test/unit/receivers_test.rb +0 -66
  257. data/test/unit/return_values_test.rb +0 -63
  258. data/test/unit/sequence_test.rb +0 -104
  259. data/test/unit/single_return_value_test.rb +0 -14
  260. data/test/unit/single_yield_test.rb +0 -18
  261. data/test/unit/state_machine_test.rb +0 -98
  262. data/test/unit/string_inspect_test.rb +0 -11
  263. data/test/unit/thrower_test.rb +0 -20
  264. data/test/unit/yield_parameters_test.rb +0 -93
  265. data/yard-templates/default/layout/html/google_analytics.erb +0 -11
  266. data/yard-templates/default/layout/html/setup.rb +0 -6
data/lib/mocha/mock.rb CHANGED
@@ -1,20 +1,26 @@
1
- require 'metaclass'
1
+ require 'ruby2_keywords'
2
2
  require 'mocha/expectation'
3
3
  require 'mocha/expectation_list'
4
+ require 'mocha/invocation'
4
5
  require 'mocha/names'
5
6
  require 'mocha/receivers'
6
7
  require 'mocha/method_matcher'
7
8
  require 'mocha/parameters_matcher'
8
- require 'mocha/unexpected_invocation'
9
9
  require 'mocha/argument_iterator'
10
10
  require 'mocha/expectation_error_factory'
11
11
 
12
12
  module Mocha
13
-
14
13
  # Traditional mock object.
15
14
  #
16
- # All methods return an {Expectation} which can be further modified by
17
- # methods on {Expectation}.
15
+ # {expects} and {stubs} return an {Expectation} which can be further modified
16
+ # by methods on {Expectation}.
17
+ #
18
+ # {responds_like} and {responds_like_instance_of} both return a {Mock}, and
19
+ # can therefore, be chained to the original creation methods in {API}.
20
+ # They force the mock to indicate what it is supposed to be mocking, thus
21
+ # making it a safer verifying mock. They check that the underlying +responder+
22
+ # will actually respond to the methods being stubbed, throwing a
23
+ # +NoMethodError+ upon invocation otherwise.
18
24
  #
19
25
  # Stubs and expectations are basically the same thing. A stub is just an
20
26
  # expectation of zero or more invocations. The {#stubs} method is syntactic
@@ -67,15 +73,14 @@ module Mocha
67
73
  # different mock objects, use the {Expectation#in_sequence} method to
68
74
  # explicitly define a total or partial ordering of invocations.
69
75
  class Mock
70
-
71
76
  # Adds an expectation that the specified method must be called exactly once with any parameters.
72
77
  #
73
- # @param [Symbol,String] method_name name of expected method
74
- # @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {#expects} were called multiple times.
78
+ # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
75
79
  #
76
80
  # @overload def expects(method_name)
81
+ # @param [Symbol,String] method_name name of expected method
77
82
  # @overload def expects(expected_methods_vs_return_values)
78
- # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
83
+ # @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {#expects} were called multiple times.
79
84
  #
80
85
  # @example Expected method invoked once so no error raised
81
86
  # object = mock()
@@ -103,24 +108,26 @@ module Mocha
103
108
  # object.expects(:expected_method_one).returns(:result_one)
104
109
  # object.expects(:expected_method_two).returns(:result_two)
105
110
  def expects(method_name_or_hash, backtrace = nil)
111
+ expectation = nil
106
112
  iterator = ArgumentIterator.new(method_name_or_hash)
107
- iterator.each { |*args|
113
+ iterator.each do |*args|
108
114
  method_name = args.shift
109
115
  ensure_method_not_already_defined(method_name)
110
116
  expectation = Expectation.new(self, method_name, backtrace)
111
- expectation.returns(args.shift) if args.length > 0
117
+ expectation.returns(args.shift) unless args.empty?
112
118
  @expectations.add(expectation)
113
- }
119
+ end
120
+ expectation
114
121
  end
115
122
 
116
123
  # Adds an expectation that the specified method may be called any number of times with any parameters.
117
124
  #
118
- # @param [Symbol,String] method_name name of stubbed method
119
- # @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {#stubs} were called multiple times.
125
+ # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
120
126
  #
121
127
  # @overload def stubs(method_name)
128
+ # @param [Symbol,String] method_name name of stubbed method
122
129
  # @overload def stubs(stubbed_methods_vs_return_values)
123
- # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
130
+ # @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {#stubs} were called multiple times.
124
131
  #
125
132
  # @example No error raised however many times stubbed method is invoked
126
133
  # object = mock()
@@ -139,36 +146,48 @@ module Mocha
139
146
  # object.stubs(:stubbed_method_one).returns(:result_one)
140
147
  # object.stubs(:stubbed_method_two).returns(:result_two)
141
148
  def stubs(method_name_or_hash, backtrace = nil)
149
+ expectation = nil
142
150
  iterator = ArgumentIterator.new(method_name_or_hash)
143
- iterator.each { |*args|
151
+ iterator.each do |*args|
144
152
  method_name = args.shift
145
153
  ensure_method_not_already_defined(method_name)
146
154
  expectation = Expectation.new(self, method_name, backtrace)
147
155
  expectation.at_least(0)
148
- expectation.returns(args.shift) if args.length > 0
156
+ expectation.returns(args.shift) unless args.empty?
149
157
  @expectations.add(expectation)
150
- }
158
+ end
159
+ expectation
151
160
  end
152
161
 
153
- # Removes the specified stubbed method (added by calls to {#expects} or {#stubs}) and all expectations associated with it.
162
+ # Removes the specified stubbed methods (added by calls to {#expects} or {#stubs}) and all expectations associated with them.
154
163
  #
155
- # @param [Symbol] method_name name of method to unstub.
164
+ # @param [Array<Symbol>] method_names names of methods to unstub.
156
165
  #
157
166
  # @example Invoking an unstubbed method causes error to be raised
158
- # object = mock('mock') do
167
+ # object = mock('mock')
159
168
  # object.stubs(:stubbed_method).returns(:result_one)
160
169
  # object.stubbed_method # => :result_one
161
170
  # object.unstub(:stubbed_method)
162
171
  # object.stubbed_method # => unexpected invocation: #<Mock:mock>.stubbed_method()
163
- def unstub(method_name)
164
- @expectations.remove_all_matching_method(method_name)
172
+ #
173
+ # @example Unstubbing multiple methods.
174
+ # multiplier.unstub(:double, :triple)
175
+ #
176
+ # # exactly equivalent to
177
+ #
178
+ # multiplier.unstub(:double)
179
+ # multiplier.unstub(:triple)
180
+ def unstub(*method_names)
181
+ method_names.each do |method_name|
182
+ @expectations.remove_all_matching_method(method_name)
183
+ end
165
184
  end
166
185
 
167
- # Constrains the {Mock} instance so that it can only expect or stub methods to which +responder+ responds. The constraint is only applied at method invocation time.
186
+ # Constrains the {Mock} instance so that it can only expect or stub methods to which +responder+ responds publicly. The constraint is only applied at method invocation time.
168
187
  #
169
- # A +NoMethodError+ will be raised if the +responder+ does not +#respond_to?+ a method invocation (even if the method has been expected or stubbed).
188
+ # A +NoMethodError+ will be raised if the +responder+ does not publicly +#respond_to?+ the invoked method (even if the method has been expected or stubbed).
170
189
  #
171
- # The {Mock} instance will delegate its +#respond_to?+ method to the +responder+.
190
+ # The {Mock} instance will delegate its +#respond_to?+ method to the +responder+. However, the +include_all+ parameter is not passed through, so only public methods on the +responder+ will be considered.
172
191
  #
173
192
  # Note that the methods on +responder+ are never actually invoked.
174
193
  #
@@ -218,11 +237,11 @@ module Mocha
218
237
  self
219
238
  end
220
239
 
221
- # Constrains the {Mock} instance so that it can only expect or stub methods to which an instance of the +responder_class+ responds. The constraint is only applied at method invocation time. Note that the responder instance is instantiated using +Class#allocate+.
240
+ # Constrains the {Mock} instance so that it can only expect or stub methods to which an instance of the +responder_class+ responds publicly. The constraint is only applied at method invocation time. Note that the responder instance is instantiated using +Class#allocate+.
222
241
  #
223
- # A +NoMethodError+ will be raised if the responder instance does not +#respond_to?+ a method invocation (even if the method has been expected or stubbed).
242
+ # A +NoMethodError+ will be raised if the responder instance does not publicly +#respond_to?+ the invoked method (even if the method has been expected or stubbed).
224
243
  #
225
- # The {Mock} instance will delegate its +#respond_to?+ method to the responder instance.
244
+ # The {Mock} instance will delegate its +#respond_to?+ method to the responder instance. However, the +include_all+ parameter is not passed through, so only public methods on the +responder+ will be considered.
226
245
  #
227
246
  # Note that the methods on the responder instance are never actually invoked.
228
247
  #
@@ -251,7 +270,7 @@ module Mocha
251
270
  end
252
271
 
253
272
  # @private
254
- def initialize(mockery, name = nil, receiver = nil, &block)
273
+ def initialize(mockery, name = nil, receiver = nil)
255
274
  @mockery = mockery
256
275
  @name = name || DefaultName.new(self)
257
276
  @receiver = receiver || DefaultReceiver.new(self)
@@ -259,7 +278,7 @@ module Mocha
259
278
  @everything_stubbed = false
260
279
  @responder = nil
261
280
  @unexpected_invocation = nil
262
- instance_eval(&block) if block
281
+ @expired = false
263
282
  end
264
283
 
265
284
  # @private
@@ -269,6 +288,8 @@ module Mocha
269
288
 
270
289
  alias_method :__stubs__, :stubs
271
290
 
291
+ alias_method :__singleton_class__, :singleton_class
292
+
272
293
  alias_method :quacks_like, :responds_like
273
294
  alias_method :quacks_like_instance_of, :responds_like_instance_of
274
295
 
@@ -288,35 +309,29 @@ module Mocha
288
309
  end
289
310
 
290
311
  # @private
312
+ # rubocop:disable Style/MethodMissingSuper
291
313
  def method_missing(symbol, *arguments, &block)
292
- if @responder and not @responder.respond_to?(symbol)
293
- raise NoMethodError, "undefined method `#{symbol}' for #{self.mocha_inspect} which responds like #{@responder.mocha_inspect}"
294
- end
295
- if matching_expectation_allowing_invocation = all_expectations.match_allowing_invocation(symbol, *arguments)
296
- matching_expectation_allowing_invocation.invoke(&block)
297
- else
298
- if (matching_expectation = all_expectations.match(symbol, *arguments)) || (!matching_expectation && !@everything_stubbed)
299
- if @unexpected_invocation.nil?
300
- @unexpected_invocation = UnexpectedInvocation.new(self, symbol, *arguments)
301
- matching_expectation.invoke(&block) if matching_expectation
302
- message = @unexpected_invocation.full_description
303
- message << @mockery.mocha_inspect
304
- else
305
- message = @unexpected_invocation.short_description
306
- end
307
- raise ExpectationErrorFactory.build(message, caller)
308
- end
314
+ handle_method_call(symbol, arguments, block)
315
+ end
316
+ ruby2_keywords(:method_missing)
317
+ # rubocop:enable Style/MethodMissingSuper
318
+
319
+ # @private
320
+ def handle_method_call(symbol, arguments, block)
321
+ check_expiry
322
+ check_responder_responds_to(symbol)
323
+ invocation = Invocation.new(self, symbol, arguments, block)
324
+ if (matching_expectation_allowing_invocation = all_expectations.match_allowing_invocation(invocation))
325
+ matching_expectation_allowing_invocation.invoke(invocation)
326
+ elsif (matching_expectation = all_expectations.match(invocation)) || (!matching_expectation && !@everything_stubbed)
327
+ raise_unexpected_invocation_error(invocation, matching_expectation)
309
328
  end
310
329
  end
311
330
 
312
331
  # @private
313
- def respond_to?(symbol, include_private = false)
314
- if @responder then
315
- if @responder.method(:respond_to?).arity > 1
316
- @responder.respond_to?(symbol, include_private)
317
- else
318
- @responder.respond_to?(symbol)
319
- end
332
+ def respond_to_missing?(symbol, _include_all)
333
+ if @responder
334
+ @responder.respond_to?(symbol)
320
335
  else
321
336
  @everything_stubbed || all_expectations.matches_method?(symbol)
322
337
  end
@@ -327,6 +342,11 @@ module Mocha
327
342
  @expectations.verified?(assertion_counter)
328
343
  end
329
344
 
345
+ # @private
346
+ def __expire__
347
+ @expired = true
348
+ end
349
+
330
350
  # @private
331
351
  def mocha_inspect
332
352
  @name.mocha_inspect
@@ -339,7 +359,7 @@ module Mocha
339
359
 
340
360
  # @private
341
361
  def ensure_method_not_already_defined(method_name)
342
- self.__metaclass__.send(:undef_method, method_name) if self.__metaclass__.method_defined?(method_name) || self.__metaclass__.private_method_defined?(method_name)
362
+ __singleton_class__.send(:undef_method, method_name) if __singleton_class__.method_defined?(method_name) || __singleton_class__.private_method_defined?(method_name)
343
363
  end
344
364
 
345
365
  # @private
@@ -347,6 +367,34 @@ module Mocha
347
367
  @expectations.any?
348
368
  end
349
369
 
350
- end
370
+ private
371
+
372
+ def raise_unexpected_invocation_error(invocation, matching_expectation)
373
+ if @unexpected_invocation.nil?
374
+ @unexpected_invocation = invocation
375
+ matching_expectation.invoke(invocation) if matching_expectation
376
+ message = "#{@unexpected_invocation.call_description}\n#{@mockery.mocha_inspect}"
377
+ else
378
+ message = @unexpected_invocation.short_call_description
379
+ end
380
+ raise ExpectationErrorFactory.build("unexpected invocation: #{message}", caller)
381
+ end
382
+
383
+ def check_responder_responds_to(symbol)
384
+ if @responder && !@responder.respond_to?(symbol) # rubocop:disable Style/GuardClause
385
+ raise NoMethodError, "undefined method `#{symbol}' for #{mocha_inspect} which responds like #{@responder.mocha_inspect}"
386
+ end
387
+ end
388
+
389
+ def check_expiry
390
+ return unless @expired
351
391
 
392
+ sentences = [
393
+ "#{mocha_inspect} was instantiated in one test but it is receiving invocations within another test.",
394
+ 'This can lead to unintended interactions between tests and hence unexpected test failures.',
395
+ 'Ensure that every test correctly cleans up any state that it introduces.'
396
+ ]
397
+ raise StubbingError.new(sentences.join(' '), caller)
398
+ end
399
+ end
352
400
  end
data/lib/mocha/mockery.rb CHANGED
@@ -6,16 +6,42 @@ require 'mocha/state_machine'
6
6
  require 'mocha/logger'
7
7
  require 'mocha/configuration'
8
8
  require 'mocha/stubbing_error'
9
+ require 'mocha/not_initialized_error'
9
10
  require 'mocha/expectation_error_factory'
10
11
 
11
12
  module Mocha
12
-
13
13
  class Mockery
14
+ class Null < self
15
+ def add_mock(*)
16
+ raise_not_initialized_error
17
+ end
14
18
 
15
- class << self
19
+ def add_state_machine(*)
20
+ raise_not_initialized_error
21
+ end
22
+
23
+ def stubba
24
+ Central::Null.new(&method(:raise_not_initialized_error))
25
+ end
16
26
 
27
+ private
28
+
29
+ def raise_not_initialized_error
30
+ message = 'Mocha methods cannot be used outside the context of a test'
31
+ raise NotInitializedError.new(message, caller)
32
+ end
33
+ end
34
+
35
+ class << self
17
36
  def instance
18
- @instance ||= new
37
+ @instances.last || Null.new
38
+ end
39
+
40
+ def setup
41
+ @instances ||= []
42
+ mockery = new
43
+ mockery.logger = instance.logger unless @instances.empty?
44
+ @instances.push(mockery)
19
45
  end
20
46
 
21
47
  def verify(*args)
@@ -24,28 +50,25 @@ module Mocha
24
50
 
25
51
  def teardown
26
52
  instance.teardown
53
+ ensure
54
+ @instances.pop
27
55
  end
28
-
29
- def reset_instance
30
- @instance = nil
31
- end
32
-
33
56
  end
34
57
 
35
- def named_mock(name, &block)
36
- add_mock(Mock.new(self, Name.new(name), &block))
58
+ def named_mock(name)
59
+ add_mock(Mock.new(self, Name.new(name)))
37
60
  end
38
61
 
39
- def unnamed_mock(&block)
40
- add_mock(Mock.new(self, &block))
62
+ def unnamed_mock
63
+ add_mock(Mock.new(self))
41
64
  end
42
65
 
43
- def mock_impersonating(object, &block)
44
- add_mock(Mock.new(self, ImpersonatingName.new(object), ObjectReceiver.new(object), &block))
66
+ def mock_impersonating(object)
67
+ add_mock(Mock.new(self, ImpersonatingName.new(object), ObjectReceiver.new(object)))
45
68
  end
46
69
 
47
- def mock_impersonating_any_instance_of(klass, &block)
48
- add_mock(Mock.new(self, ImpersonatingAnyInstanceName.new(klass), AnyInstanceReceiver.new(klass), &block))
70
+ def mock_impersonating_any_instance_of(klass)
71
+ add_mock(Mock.new(self, ImpersonatingAnyInstanceName.new(klass), AnyInstanceReceiver.new(klass)))
49
72
  end
50
73
 
51
74
  def new_state_machine(name)
@@ -55,24 +78,22 @@ module Mocha
55
78
  def verify(assertion_counter = nil)
56
79
  unless mocks.all? { |mock| mock.__verified__?(assertion_counter) }
57
80
  message = "not all expectations were satisfied\n#{mocha_inspect}"
58
- if unsatisfied_expectations.empty?
59
- backtrace = caller
60
- else
61
- backtrace = unsatisfied_expectations[0].backtrace
62
- end
81
+ backtrace = if unsatisfied_expectations.empty?
82
+ caller
83
+ else
84
+ unsatisfied_expectations[0].backtrace
85
+ end
63
86
  raise ExpectationErrorFactory.build(message, backtrace)
64
87
  end
65
- expectations.each do |e|
66
- unless Mocha::Configuration.allow?(:stubbing_method_unnecessarily)
67
- unless e.used?
68
- on_stubbing_method_unnecessarily(e)
69
- end
70
- end
88
+ expectations.reject(&:used?).each do |expectation|
89
+ signature_proc = lambda { expectation.method_signature }
90
+ check(:stubbing_method_unnecessarily, 'method unnecessarily', signature_proc, expectation.backtrace)
71
91
  end
72
92
  end
73
93
 
74
94
  def teardown
75
95
  stubba.unstub_all
96
+ mocks.each(&:__expire__)
76
97
  reset
77
98
  end
78
99
 
@@ -89,78 +110,23 @@ module Mocha
89
110
  end
90
111
 
91
112
  def mocha_inspect
92
- message = ""
93
- message << "unsatisfied expectations:\n- #{unsatisfied_expectations.map { |e| e.mocha_inspect }.join("\n- ")}\n" unless unsatisfied_expectations.empty?
94
- message << "satisfied expectations:\n- #{satisfied_expectations.map { |e| e.mocha_inspect }.join("\n- ")}\n" unless satisfied_expectations.empty?
95
- message << "states:\n- #{state_machines.map { |sm| sm.mocha_inspect }.join("\n- ")}" unless state_machines.empty?
113
+ message = ''
114
+ message << "unsatisfied expectations:\n- #{unsatisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if unsatisfied_expectations.any?
115
+ message << "satisfied expectations:\n- #{satisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if satisfied_expectations.any?
116
+ message << "states:\n- #{state_machines.map(&:mocha_inspect).join("\n- ")}\n" if state_machines.any?
96
117
  message
97
118
  end
98
119
 
99
120
  def on_stubbing(object, method)
100
- method = RUBY_VERSION < '1.9' ? method.to_s : method.to_sym
101
- unless Mocha::Configuration.allow?(:stubbing_non_existent_method)
102
- unless object.method_exists?(method, include_public_methods = true)
103
- on_stubbing_non_existent_method(object, method)
104
- end
105
- end
106
- unless Mocha::Configuration.allow?(:stubbing_non_public_method)
107
- if object.method_exists?(method, include_public_methods = false)
108
- on_stubbing_non_public_method(object, method)
109
- end
110
- end
111
- unless Mocha::Configuration.allow?(:stubbing_method_on_nil)
112
- if object.nil?
113
- on_stubbing_method_on_nil(object, method)
114
- end
115
- end
116
- unless Mocha::Configuration.allow?(:stubbing_method_on_non_mock_object)
117
- on_stubbing_method_on_non_mock_object(object, method)
118
- end
119
- end
120
-
121
- def on_stubbing_non_existent_method(object, method)
122
- if Mocha::Configuration.prevent?(:stubbing_non_existent_method)
123
- raise StubbingError.new("stubbing non-existent method: #{object.mocha_inspect}.#{method}", caller)
121
+ signature_proc = lambda { "#{object.mocha_inspect}.#{method}" }
122
+ check(:stubbing_non_existent_method, 'non-existent method', signature_proc) do
123
+ !(object.stubba_class.__method_exists__?(method, true) || object.respond_to?(method))
124
124
  end
125
- if Mocha::Configuration.warn_when?(:stubbing_non_existent_method)
126
- logger.warn "stubbing non-existent method: #{object.mocha_inspect}.#{method}"
127
- end
128
- end
129
-
130
- def on_stubbing_non_public_method(object, method)
131
- if Mocha::Configuration.prevent?(:stubbing_non_public_method)
132
- raise StubbingError.new("stubbing non-public method: #{object.mocha_inspect}.#{method}", caller)
133
- end
134
- if Mocha::Configuration.warn_when?(:stubbing_non_public_method)
135
- logger.warn "stubbing non-public method: #{object.mocha_inspect}.#{method}"
136
- end
137
- end
138
-
139
- def on_stubbing_method_on_nil(object, method)
140
- if Mocha::Configuration.prevent?(:stubbing_method_on_nil)
141
- raise StubbingError.new("stubbing method on nil: #{object.mocha_inspect}.#{method}", caller)
142
- end
143
- if Mocha::Configuration.warn_when?(:stubbing_method_on_nil)
144
- logger.warn "stubbing method on nil: #{object.mocha_inspect}.#{method}"
145
- end
146
- end
147
-
148
- def on_stubbing_method_on_non_mock_object(object, method)
149
- if Mocha::Configuration.prevent?(:stubbing_method_on_non_mock_object)
150
- raise StubbingError.new("stubbing method on non-mock object: #{object.mocha_inspect}.#{method}", caller)
151
- end
152
- if Mocha::Configuration.warn_when?(:stubbing_method_on_non_mock_object)
153
- logger.warn "stubbing method on non-mock object: #{object.mocha_inspect}.#{method}"
154
- end
155
- end
156
-
157
- def on_stubbing_method_unnecessarily(expectation)
158
- if Mocha::Configuration.prevent?(:stubbing_method_unnecessarily)
159
- raise StubbingError.new("stubbing method unnecessarily: #{expectation.method_signature}", expectation.backtrace)
160
- end
161
- if Mocha::Configuration.warn_when?(:stubbing_method_unnecessarily)
162
- logger.warn "stubbing method unnecessarily: #{expectation.method_signature}"
125
+ check(:stubbing_non_public_method, 'non-public method', signature_proc) do
126
+ object.stubba_class.__method_exists__?(method, false)
163
127
  end
128
+ check(:stubbing_method_on_nil, 'method on nil', signature_proc) { object.nil? }
129
+ check(:stubbing_method_on_non_mock_object, 'method on non-mock object', signature_proc)
164
130
  end
165
131
 
166
132
  attr_writer :logger
@@ -169,19 +135,27 @@ module Mocha
169
135
  @logger ||= Logger.new($stderr)
170
136
  end
171
137
 
172
-
173
138
  private
174
139
 
140
+ def check(action, description, signature_proc, backtrace = caller)
141
+ treatment = Mocha.configuration.send(action)
142
+ return if (treatment == :allow) || (block_given? && !yield)
143
+ method_signature = signature_proc.call
144
+ message = "stubbing #{description}: #{method_signature}"
145
+ raise StubbingError.new(message, backtrace) if treatment == :prevent
146
+ logger.warn(message) if treatment == :warn
147
+ end
148
+
175
149
  def expectations
176
150
  mocks.map { |mock| mock.__expectations__.to_a }.flatten
177
151
  end
178
152
 
179
153
  def unsatisfied_expectations
180
- expectations.reject { |e| e.verified? }
154
+ expectations.reject(&:verified?)
181
155
  end
182
156
 
183
157
  def satisfied_expectations
184
- expectations.select { |e| e.verified? }
158
+ expectations.select(&:verified?)
185
159
  end
186
160
 
187
161
  def add_mock(mock)
@@ -199,7 +173,5 @@ module Mocha
199
173
  @mocks = nil
200
174
  @state_machines = nil
201
175
  end
202
-
203
176
  end
204
-
205
177
  end
data/lib/mocha/names.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  module Mocha
2
-
3
2
  class ImpersonatingName
4
-
5
3
  def initialize(object)
6
4
  @object = object
7
5
  end
@@ -9,11 +7,9 @@ module Mocha
9
7
  def mocha_inspect
10
8
  @object.mocha_inspect
11
9
  end
12
-
13
10
  end
14
11
 
15
12
  class ImpersonatingAnyInstanceName
16
-
17
13
  def initialize(klass)
18
14
  @klass = klass
19
15
  end
@@ -21,23 +17,19 @@ module Mocha
21
17
  def mocha_inspect
22
18
  "#<AnyInstance:#{@klass.mocha_inspect}>"
23
19
  end
24
-
25
20
  end
26
21
 
27
22
  class Name
28
-
29
23
  def initialize(name)
30
- @name = name
24
+ @name = name.to_s
31
25
  end
32
26
 
33
27
  def mocha_inspect
34
28
  "#<Mock:#{@name}>"
35
29
  end
36
-
37
30
  end
38
31
 
39
32
  class DefaultName
40
-
41
33
  def initialize(mock)
42
34
  @mock = mock
43
35
  end
@@ -45,9 +37,7 @@ module Mocha
45
37
  def mocha_inspect
46
38
  address = @mock.__id__ * 2
47
39
  address += 0x100000000 if address < 0
48
- "#<Mock:0x#{'%x' % address}>"
40
+ "#<Mock:0x#{format('%<address>x', address: address)}>"
49
41
  end
50
-
51
42
  end
52
-
53
43
  end
@@ -0,0 +1,7 @@
1
+ require 'mocha/error_with_filtered_backtrace'
2
+
3
+ module Mocha
4
+ # Exception raised when Mocha has not been initialized, e.g. outside the
5
+ # context of a test.
6
+ class NotInitializedError < ErrorWithFilteredBacktrace; end
7
+ end