mocha 1.2.1 → 2.0.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 +65 -0
  4. data/.rubocop_todo.yml +27 -0
  5. data/.yardopts +1 -0
  6. data/CONTRIBUTING.md +4 -9
  7. data/Gemfile +29 -0
  8. data/README.md +110 -106
  9. data/RELEASE.md +306 -1
  10. data/Rakefile +52 -45
  11. data/gemfiles/Gemfile.minitest.latest +1 -0
  12. data/gemfiles/Gemfile.test-unit.latest +2 -5
  13. data/lib/mocha/any_instance_method.rb +9 -62
  14. data/lib/mocha/api.rb +84 -68
  15. data/lib/mocha/argument_iterator.rb +4 -8
  16. data/lib/mocha/backtrace_filter.rb +1 -5
  17. data/lib/mocha/block_matcher.rb +31 -0
  18. data/lib/mocha/cardinality.rb +60 -49
  19. data/lib/mocha/central.rb +21 -12
  20. data/lib/mocha/change_state_side_effect.rb +0 -4
  21. data/lib/mocha/class_methods.rb +19 -21
  22. data/lib/mocha/configuration.rb +312 -47
  23. data/lib/mocha/debug.rb +3 -2
  24. data/lib/mocha/deprecation.rb +8 -11
  25. data/lib/mocha/detection/mini_test.rb +0 -2
  26. data/lib/mocha/detection/test_unit.rb +3 -5
  27. data/lib/mocha/error_with_filtered_backtrace.rb +13 -0
  28. data/lib/mocha/exception_raiser.rb +4 -6
  29. data/lib/mocha/expectation.rb +186 -95
  30. data/lib/mocha/expectation_error.rb +1 -1
  31. data/lib/mocha/expectation_error_factory.rb +0 -1
  32. data/lib/mocha/expectation_list.rb +7 -11
  33. data/lib/mocha/hooks.rb +1 -3
  34. data/lib/mocha/in_state_ordering_constraint.rb +0 -4
  35. data/lib/mocha/inspect.rb +30 -38
  36. data/lib/mocha/instance_method.rb +11 -8
  37. data/lib/mocha/integration/mini_test/adapter.rb +2 -4
  38. data/lib/mocha/integration/mini_test/exception_translation.rb +1 -1
  39. data/lib/mocha/integration/mini_test.rb +10 -31
  40. data/lib/mocha/integration/monkey_patcher.rb +5 -7
  41. data/lib/mocha/integration/test_unit/adapter.rb +5 -6
  42. data/lib/mocha/integration/test_unit.rb +10 -26
  43. data/lib/mocha/invocation.rb +73 -0
  44. data/lib/mocha/is_a.rb +0 -2
  45. data/lib/mocha/logger.rb +0 -4
  46. data/lib/mocha/macos_version.rb +5 -0
  47. data/lib/mocha/method_matcher.rb +1 -5
  48. data/lib/mocha/minitest.rb +6 -0
  49. data/lib/mocha/mock.rb +99 -51
  50. data/lib/mocha/mockery.rb +70 -99
  51. data/lib/mocha/names.rb +2 -12
  52. data/lib/mocha/not_initialized_error.rb +7 -0
  53. data/lib/mocha/object_methods.rb +25 -31
  54. data/lib/mocha/parameter_matchers/all_of.rb +2 -8
  55. data/lib/mocha/parameter_matchers/any_of.rb +2 -8
  56. data/lib/mocha/parameter_matchers/any_parameters.rb +3 -9
  57. data/lib/mocha/parameter_matchers/anything.rb +2 -8
  58. data/lib/mocha/parameter_matchers/base.rb +7 -13
  59. data/lib/mocha/parameter_matchers/equals.rb +0 -6
  60. data/lib/mocha/parameter_matchers/{query_string.rb → equivalent_uri.rb} +14 -15
  61. data/lib/mocha/parameter_matchers/has_entries.rb +2 -7
  62. data/lib/mocha/parameter_matchers/has_entry.rb +26 -21
  63. data/lib/mocha/parameter_matchers/has_key.rb +2 -7
  64. data/lib/mocha/parameter_matchers/has_keys.rb +53 -0
  65. data/lib/mocha/parameter_matchers/has_value.rb +2 -7
  66. data/lib/mocha/parameter_matchers/includes.rb +6 -7
  67. data/lib/mocha/parameter_matchers/instance_methods.rb +27 -0
  68. data/lib/mocha/parameter_matchers/instance_of.rb +0 -6
  69. data/lib/mocha/parameter_matchers/is_a.rb +2 -7
  70. data/lib/mocha/parameter_matchers/kind_of.rb +2 -6
  71. data/lib/mocha/parameter_matchers/not.rb +2 -7
  72. data/lib/mocha/parameter_matchers/optionally.rb +4 -10
  73. data/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb +64 -0
  74. data/lib/mocha/parameter_matchers/regexp_matches.rb +0 -6
  75. data/lib/mocha/parameter_matchers/responds_with.rb +3 -8
  76. data/lib/mocha/parameter_matchers/yaml_equivalent.rb +2 -6
  77. data/lib/mocha/parameter_matchers.rb +3 -4
  78. data/lib/mocha/parameters_matcher.rb +8 -11
  79. data/lib/mocha/raised_exception.rb +11 -0
  80. data/lib/mocha/receivers.rb +10 -14
  81. data/lib/mocha/return_values.rb +4 -8
  82. data/lib/mocha/ruby_version.rb +1 -2
  83. data/lib/mocha/sequence.rb +4 -9
  84. data/lib/mocha/single_return_value.rb +2 -5
  85. data/lib/mocha/state_machine.rb +33 -46
  86. data/lib/mocha/stubbed_method.rb +88 -0
  87. data/lib/mocha/stubbing_error.rb +2 -13
  88. data/lib/mocha/test_unit.rb +5 -2
  89. data/lib/mocha/thrower.rb +4 -6
  90. data/lib/mocha/thrown_object.rb +12 -0
  91. data/lib/mocha/version.rb +1 -1
  92. data/lib/mocha/yield_parameters.rb +7 -17
  93. data/mocha.gemspec +14 -65
  94. data/yard-templates/default/layout/html/google_analytics.erb +6 -9
  95. data/yard-templates/default/layout/html/setup.rb +2 -3
  96. metadata +26 -266
  97. data/bin/build-matrix +0 -70
  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 -119
  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_272_test.rb +0 -52
  151. data/test/acceptance/issue_65_test.rb +0 -63
  152. data/test/acceptance/issue_70_test.rb +0 -55
  153. data/test/acceptance/mocha_example_test.rb +0 -98
  154. data/test/acceptance/mocha_test_result_test.rb +0 -84
  155. data/test/acceptance/mock_test.rb +0 -100
  156. data/test/acceptance/mock_with_initializer_block_test.rb +0 -51
  157. data/test/acceptance/mocked_methods_dispatch_test.rb +0 -78
  158. data/test/acceptance/multiple_expectations_failure_message_test.rb +0 -68
  159. data/test/acceptance/optional_parameters_test.rb +0 -70
  160. data/test/acceptance/parameter_matcher_test.rb +0 -337
  161. data/test/acceptance/partial_mocks_test.rb +0 -47
  162. data/test/acceptance/prepend_test.rb +0 -89
  163. data/test/acceptance/raise_exception_test.rb +0 -39
  164. data/test/acceptance/return_value_test.rb +0 -52
  165. data/test/acceptance/sequence_test.rb +0 -192
  166. data/test/acceptance/states_test.rb +0 -70
  167. data/test/acceptance/stub_any_instance_method_defined_on_superclass_test.rb +0 -34
  168. data/test/acceptance/stub_any_instance_method_test.rb +0 -280
  169. data/test/acceptance/stub_class_method_defined_on_active_record_association_proxy_test.rb +0 -106
  170. data/test/acceptance/stub_class_method_defined_on_class_test.rb +0 -78
  171. data/test/acceptance/stub_class_method_defined_on_module_test.rb +0 -75
  172. data/test/acceptance/stub_class_method_defined_on_superclass_test.rb +0 -112
  173. data/test/acceptance/stub_everything_test.rb +0 -56
  174. data/test/acceptance/stub_instance_method_defined_on_active_record_association_proxy_test.rb +0 -93
  175. data/test/acceptance/stub_instance_method_defined_on_class_and_aliased_test.rb +0 -69
  176. data/test/acceptance/stub_instance_method_defined_on_class_test.rb +0 -69
  177. data/test/acceptance/stub_instance_method_defined_on_kernel_module_test.rb +0 -75
  178. data/test/acceptance/stub_instance_method_defined_on_module_test.rb +0 -78
  179. data/test/acceptance/stub_instance_method_defined_on_object_class_test.rb +0 -75
  180. data/test/acceptance/stub_instance_method_defined_on_singleton_class_test.rb +0 -70
  181. data/test/acceptance/stub_instance_method_defined_on_superclass_test.rb +0 -72
  182. data/test/acceptance/stub_method_defined_on_module_and_aliased_test.rb +0 -39
  183. data/test/acceptance/stub_module_method_test.rb +0 -163
  184. data/test/acceptance/stub_test.rb +0 -52
  185. data/test/acceptance/stubba_example_test.rb +0 -102
  186. data/test/acceptance/stubba_test_result_test.rb +0 -66
  187. data/test/acceptance/stubbing_error_backtrace_test.rb +0 -64
  188. data/test/acceptance/stubbing_frozen_object_test.rb +0 -88
  189. data/test/acceptance/stubbing_method_accepting_block_parameter_test.rb +0 -48
  190. data/test/acceptance/stubbing_method_unnecessarily_test.rb +0 -65
  191. data/test/acceptance/stubbing_nil_test.rb +0 -61
  192. data/test/acceptance/stubbing_non_existent_any_instance_method_test.rb +0 -143
  193. data/test/acceptance/stubbing_non_existent_class_method_test.rb +0 -157
  194. data/test/acceptance/stubbing_non_existent_instance_method_test.rb +0 -147
  195. data/test/acceptance/stubbing_non_public_any_instance_method_test.rb +0 -130
  196. data/test/acceptance/stubbing_non_public_class_method_test.rb +0 -163
  197. data/test/acceptance/stubbing_non_public_instance_method_test.rb +0 -143
  198. data/test/acceptance/stubbing_on_non_mock_object_test.rb +0 -64
  199. data/test/acceptance/stubbing_same_class_method_on_parent_and_child_classes_test.rb +0 -35
  200. data/test/acceptance/throw_test.rb +0 -45
  201. data/test/acceptance/unexpected_invocation_test.rb +0 -25
  202. data/test/acceptance/unstubbing_test.rb +0 -168
  203. data/test/assertions.rb +0 -8
  204. data/test/deprecation_disabler.rb +0 -15
  205. data/test/execution_point.rb +0 -36
  206. data/test/integration/mini_test_test.rb +0 -8
  207. data/test/integration/shared_tests.rb +0 -174
  208. data/test/integration/test_unit_test.rb +0 -8
  209. data/test/method_definer.rb +0 -24
  210. data/test/mini_test_result.rb +0 -90
  211. data/test/minitest_result.rb +0 -49
  212. data/test/simple_counter.rb +0 -13
  213. data/test/test_helper.rb +0 -50
  214. data/test/test_runner.rb +0 -58
  215. data/test/test_unit_result.rb +0 -20
  216. data/test/unit/any_instance_method_test.rb +0 -134
  217. data/test/unit/array_inspect_test.rb +0 -16
  218. data/test/unit/backtrace_filter_test.rb +0 -19
  219. data/test/unit/cardinality_test.rb +0 -56
  220. data/test/unit/central_test.rb +0 -100
  221. data/test/unit/change_state_side_effect_test.rb +0 -41
  222. data/test/unit/class_method_test.rb +0 -225
  223. data/test/unit/class_methods_test.rb +0 -40
  224. data/test/unit/configuration_test.rb +0 -38
  225. data/test/unit/date_time_inspect_test.rb +0 -21
  226. data/test/unit/exception_raiser_test.rb +0 -42
  227. data/test/unit/expectation_list_test.rb +0 -82
  228. data/test/unit/expectation_test.rb +0 -497
  229. data/test/unit/hash_inspect_test.rb +0 -16
  230. data/test/unit/hooks_test.rb +0 -29
  231. data/test/unit/in_state_ordering_constraint_test.rb +0 -43
  232. data/test/unit/method_matcher_test.rb +0 -28
  233. data/test/unit/mock_test.rb +0 -342
  234. data/test/unit/mockery_test.rb +0 -151
  235. data/test/unit/module_methods_test.rb +0 -19
  236. data/test/unit/multiple_yields_test.rb +0 -18
  237. data/test/unit/no_yields_test.rb +0 -18
  238. data/test/unit/object_inspect_test.rb +0 -39
  239. data/test/unit/object_methods_test.rb +0 -46
  240. data/test/unit/parameter_matchers/all_of_test.rb +0 -26
  241. data/test/unit/parameter_matchers/any_of_test.rb +0 -26
  242. data/test/unit/parameter_matchers/anything_test.rb +0 -21
  243. data/test/unit/parameter_matchers/equals_test.rb +0 -25
  244. data/test/unit/parameter_matchers/has_entries_test.rb +0 -51
  245. data/test/unit/parameter_matchers/has_entry_test.rb +0 -129
  246. data/test/unit/parameter_matchers/has_key_test.rb +0 -55
  247. data/test/unit/parameter_matchers/has_value_test.rb +0 -57
  248. data/test/unit/parameter_matchers/includes_test.rb +0 -102
  249. data/test/unit/parameter_matchers/instance_of_test.rb +0 -25
  250. data/test/unit/parameter_matchers/is_a_test.rb +0 -25
  251. data/test/unit/parameter_matchers/kind_of_test.rb +0 -25
  252. data/test/unit/parameter_matchers/not_test.rb +0 -26
  253. data/test/unit/parameter_matchers/regexp_matches_test.rb +0 -46
  254. data/test/unit/parameter_matchers/responds_with_test.rb +0 -32
  255. data/test/unit/parameter_matchers/stub_matcher.rb +0 -27
  256. data/test/unit/parameter_matchers/yaml_equivalent_test.rb +0 -25
  257. data/test/unit/parameters_matcher_test.rb +0 -121
  258. data/test/unit/receivers_test.rb +0 -66
  259. data/test/unit/return_values_test.rb +0 -63
  260. data/test/unit/sequence_test.rb +0 -104
  261. data/test/unit/single_return_value_test.rb +0 -14
  262. data/test/unit/single_yield_test.rb +0 -18
  263. data/test/unit/state_machine_test.rb +0 -98
  264. data/test/unit/string_inspect_test.rb +0 -11
  265. data/test/unit/thrower_test.rb +0 -20
  266. data/test/unit/yield_parameters_test.rb +0 -93
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,29 +146,41 @@ 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
186
  # 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.
@@ -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, include_all)
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
@@ -1,4 +1,3 @@
1
- require 'mocha/ruby_version'
2
1
  require 'mocha/central'
3
2
  require 'mocha/mock'
4
3
  require 'mocha/names'
@@ -7,16 +6,42 @@ require 'mocha/state_machine'
7
6
  require 'mocha/logger'
8
7
  require 'mocha/configuration'
9
8
  require 'mocha/stubbing_error'
9
+ require 'mocha/not_initialized_error'
10
10
  require 'mocha/expectation_error_factory'
11
11
 
12
12
  module Mocha
13
-
14
13
  class Mockery
14
+ class Null < self
15
+ def add_mock(*)
16
+ raise_not_initialized_error
17
+ end
15
18
 
16
- 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
17
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
18
36
  def instance
19
- @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)
20
45
  end
21
46
 
22
47
  def verify(*args)
@@ -25,28 +50,25 @@ module Mocha
25
50
 
26
51
  def teardown
27
52
  instance.teardown
53
+ ensure
54
+ @instances.pop
28
55
  end
29
-
30
- def reset_instance
31
- @instance = nil
32
- end
33
-
34
56
  end
35
57
 
36
- def named_mock(name, &block)
37
- add_mock(Mock.new(self, Name.new(name), &block))
58
+ def named_mock(name)
59
+ add_mock(Mock.new(self, Name.new(name)))
38
60
  end
39
61
 
40
- def unnamed_mock(&block)
41
- add_mock(Mock.new(self, &block))
62
+ def unnamed_mock
63
+ add_mock(Mock.new(self))
42
64
  end
43
65
 
44
- def mock_impersonating(object, &block)
45
- 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)))
46
68
  end
47
69
 
48
- def mock_impersonating_any_instance_of(klass, &block)
49
- 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)))
50
72
  end
51
73
 
52
74
  def new_state_machine(name)
@@ -56,24 +78,22 @@ module Mocha
56
78
  def verify(assertion_counter = nil)
57
79
  unless mocks.all? { |mock| mock.__verified__?(assertion_counter) }
58
80
  message = "not all expectations were satisfied\n#{mocha_inspect}"
59
- if unsatisfied_expectations.empty?
60
- backtrace = caller
61
- else
62
- backtrace = unsatisfied_expectations[0].backtrace
63
- end
81
+ backtrace = if unsatisfied_expectations.empty?
82
+ caller
83
+ else
84
+ unsatisfied_expectations[0].backtrace
85
+ end
64
86
  raise ExpectationErrorFactory.build(message, backtrace)
65
87
  end
66
- expectations.each do |e|
67
- unless Mocha::Configuration.allow?(:stubbing_method_unnecessarily)
68
- unless e.used?
69
- on_stubbing_method_unnecessarily(e)
70
- end
71
- 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)
72
91
  end
73
92
  end
74
93
 
75
94
  def teardown
76
95
  stubba.unstub_all
96
+ mocks.each(&:__expire__)
77
97
  reset
78
98
  end
79
99
 
@@ -90,78 +110,23 @@ module Mocha
90
110
  end
91
111
 
92
112
  def mocha_inspect
93
- message = ""
94
- message << "unsatisfied expectations:\n- #{unsatisfied_expectations.map { |e| e.mocha_inspect }.join("\n- ")}\n" unless unsatisfied_expectations.empty?
95
- message << "satisfied expectations:\n- #{satisfied_expectations.map { |e| e.mocha_inspect }.join("\n- ")}\n" unless satisfied_expectations.empty?
96
- 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?
97
117
  message
98
118
  end
99
119
 
100
120
  def on_stubbing(object, method)
101
- method = PRE_RUBY_V19 ? method.to_s : method.to_sym
102
- unless Mocha::Configuration.allow?(:stubbing_non_existent_method)
103
- unless object.method_exists?(method, include_public_methods = true)
104
- on_stubbing_non_existent_method(object, method)
105
- end
106
- end
107
- unless Mocha::Configuration.allow?(:stubbing_non_public_method)
108
- if object.method_exists?(method, include_public_methods = false)
109
- on_stubbing_non_public_method(object, method)
110
- end
111
- end
112
- unless Mocha::Configuration.allow?(:stubbing_method_on_nil)
113
- if object.nil?
114
- on_stubbing_method_on_nil(object, method)
115
- end
116
- end
117
- unless Mocha::Configuration.allow?(:stubbing_method_on_non_mock_object)
118
- on_stubbing_method_on_non_mock_object(object, method)
119
- end
120
- end
121
-
122
- def on_stubbing_non_existent_method(object, method)
123
- if Mocha::Configuration.prevent?(:stubbing_non_existent_method)
124
- 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))
125
124
  end
126
- if Mocha::Configuration.warn_when?(:stubbing_non_existent_method)
127
- logger.warn "stubbing non-existent method: #{object.mocha_inspect}.#{method}"
128
- end
129
- end
130
-
131
- def on_stubbing_non_public_method(object, method)
132
- if Mocha::Configuration.prevent?(:stubbing_non_public_method)
133
- raise StubbingError.new("stubbing non-public method: #{object.mocha_inspect}.#{method}", caller)
134
- end
135
- if Mocha::Configuration.warn_when?(:stubbing_non_public_method)
136
- logger.warn "stubbing non-public method: #{object.mocha_inspect}.#{method}"
137
- end
138
- end
139
-
140
- def on_stubbing_method_on_nil(object, method)
141
- if Mocha::Configuration.prevent?(:stubbing_method_on_nil)
142
- raise StubbingError.new("stubbing method on nil: #{object.mocha_inspect}.#{method}", caller)
143
- end
144
- if Mocha::Configuration.warn_when?(:stubbing_method_on_nil)
145
- logger.warn "stubbing method on nil: #{object.mocha_inspect}.#{method}"
146
- end
147
- end
148
-
149
- def on_stubbing_method_on_non_mock_object(object, method)
150
- if Mocha::Configuration.prevent?(:stubbing_method_on_non_mock_object)
151
- raise StubbingError.new("stubbing method on non-mock object: #{object.mocha_inspect}.#{method}", caller)
152
- end
153
- if Mocha::Configuration.warn_when?(:stubbing_method_on_non_mock_object)
154
- logger.warn "stubbing method on non-mock object: #{object.mocha_inspect}.#{method}"
155
- end
156
- end
157
-
158
- def on_stubbing_method_unnecessarily(expectation)
159
- if Mocha::Configuration.prevent?(:stubbing_method_unnecessarily)
160
- raise StubbingError.new("stubbing method unnecessarily: #{expectation.method_signature}", expectation.backtrace)
161
- end
162
- if Mocha::Configuration.warn_when?(:stubbing_method_unnecessarily)
163
- 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)
164
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)
165
130
  end
166
131
 
167
132
  attr_writer :logger
@@ -170,19 +135,27 @@ module Mocha
170
135
  @logger ||= Logger.new($stderr)
171
136
  end
172
137
 
173
-
174
138
  private
175
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
+
176
149
  def expectations
177
150
  mocks.map { |mock| mock.__expectations__.to_a }.flatten
178
151
  end
179
152
 
180
153
  def unsatisfied_expectations
181
- expectations.reject { |e| e.verified? }
154
+ expectations.reject(&:verified?)
182
155
  end
183
156
 
184
157
  def satisfied_expectations
185
- expectations.select { |e| e.verified? }
158
+ expectations.select(&:verified?)
186
159
  end
187
160
 
188
161
  def add_mock(mock)
@@ -200,7 +173,5 @@ module Mocha
200
173
  @mocks = nil
201
174
  @state_machines = nil
202
175
  end
203
-
204
176
  end
205
-
206
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('%x', 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