mocha 1.4.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 (269) 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 +88 -117
  11. data/RELEASE.md +340 -0
  12. data/Rakefile +53 -52
  13. data/gemfiles/Gemfile.minitest.latest +1 -0
  14. data/gemfiles/Gemfile.test-unit.latest +2 -5
  15. data/lib/mocha/any_instance_method.rb +9 -62
  16. data/lib/mocha/api.rb +84 -74
  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 -21
  24. data/lib/mocha/configuration.rb +317 -52
  25. data/lib/mocha/debug.rb +2 -4
  26. data/lib/mocha/deprecation.rb +6 -12
  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 -28
  38. data/lib/mocha/instance_method.rb +14 -3
  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 +5 -7
  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 +5 -2
  51. data/lib/mocha/mock.rb +104 -68
  52. data/lib/mocha/mockery.rb +70 -99
  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 +0 -6
  62. data/lib/mocha/parameter_matchers/equivalent_uri.rb +3 -13
  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 +4 -6
  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 +2 -3
  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 +1 -2
  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 +15 -65
  96. metadata +27 -257
  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 -5
  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 -58
  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 -301
  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 -201
  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 -75
  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 -76
  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 -16
  205. data/test/execution_point.rb +0 -38
  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 -136
  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 -229
  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 -350
  234. data/test/unit/mockery_test.rb +0 -157
  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/equivalent_uri_test.rb +0 -51
  245. data/test/unit/parameter_matchers/has_entries_test.rb +0 -51
  246. data/test/unit/parameter_matchers/has_entry_test.rb +0 -129
  247. data/test/unit/parameter_matchers/has_key_test.rb +0 -55
  248. data/test/unit/parameter_matchers/has_value_test.rb +0 -57
  249. data/test/unit/parameter_matchers/includes_test.rb +0 -107
  250. data/test/unit/parameter_matchers/instance_of_test.rb +0 -25
  251. data/test/unit/parameter_matchers/is_a_test.rb +0 -25
  252. data/test/unit/parameter_matchers/kind_of_test.rb +0 -25
  253. data/test/unit/parameter_matchers/not_test.rb +0 -26
  254. data/test/unit/parameter_matchers/regexp_matches_test.rb +0 -46
  255. data/test/unit/parameter_matchers/responds_with_test.rb +0 -32
  256. data/test/unit/parameter_matchers/stub_matcher.rb +0 -27
  257. data/test/unit/parameter_matchers/yaml_equivalent_test.rb +0 -25
  258. data/test/unit/parameters_matcher_test.rb +0 -121
  259. data/test/unit/receivers_test.rb +0 -66
  260. data/test/unit/return_values_test.rb +0 -63
  261. data/test/unit/sequence_test.rb +0 -104
  262. data/test/unit/single_return_value_test.rb +0 -14
  263. data/test/unit/single_yield_test.rb +0 -18
  264. data/test/unit/state_machine_test.rb +0 -98
  265. data/test/unit/string_inspect_test.rb +0 -11
  266. data/test/unit/thrower_test.rb +0 -20
  267. data/test/unit/yield_parameters_test.rb +0 -93
  268. data/yard-templates/default/layout/html/google_analytics.erb +0 -11
  269. data/yard-templates/default/layout/html/setup.rb +0 -6
data/lib/mocha/mock.rb CHANGED
@@ -1,22 +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
- require 'mocha/deprecation'
12
- require 'mocha/ruby_version'
13
11
 
14
12
  module Mocha
15
-
16
13
  # Traditional mock object.
17
14
  #
18
- # All methods return an {Expectation} which can be further modified by
19
- # 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.
20
24
  #
21
25
  # Stubs and expectations are basically the same thing. A stub is just an
22
26
  # expectation of zero or more invocations. The {#stubs} method is syntactic
@@ -69,15 +73,14 @@ module Mocha
69
73
  # different mock objects, use the {Expectation#in_sequence} method to
70
74
  # explicitly define a total or partial ordering of invocations.
71
75
  class Mock
72
-
73
76
  # Adds an expectation that the specified method must be called exactly once with any parameters.
74
77
  #
75
- # @param [Symbol,String] method_name name of expected method
76
- # @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}.
77
79
  #
78
80
  # @overload def expects(method_name)
81
+ # @param [Symbol,String] method_name name of expected method
79
82
  # @overload def expects(expected_methods_vs_return_values)
80
- # @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.
81
84
  #
82
85
  # @example Expected method invoked once so no error raised
83
86
  # object = mock()
@@ -105,24 +108,26 @@ module Mocha
105
108
  # object.expects(:expected_method_one).returns(:result_one)
106
109
  # object.expects(:expected_method_two).returns(:result_two)
107
110
  def expects(method_name_or_hash, backtrace = nil)
111
+ expectation = nil
108
112
  iterator = ArgumentIterator.new(method_name_or_hash)
109
- iterator.each { |*args|
113
+ iterator.each do |*args|
110
114
  method_name = args.shift
111
115
  ensure_method_not_already_defined(method_name)
112
116
  expectation = Expectation.new(self, method_name, backtrace)
113
- expectation.returns(args.shift) if args.length > 0
117
+ expectation.returns(args.shift) unless args.empty?
114
118
  @expectations.add(expectation)
115
- }
119
+ end
120
+ expectation
116
121
  end
117
122
 
118
123
  # Adds an expectation that the specified method may be called any number of times with any parameters.
119
124
  #
120
- # @param [Symbol,String] method_name name of stubbed method
121
- # @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}.
122
126
  #
123
127
  # @overload def stubs(method_name)
128
+ # @param [Symbol,String] method_name name of stubbed method
124
129
  # @overload def stubs(stubbed_methods_vs_return_values)
125
- # @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.
126
131
  #
127
132
  # @example No error raised however many times stubbed method is invoked
128
133
  # object = mock()
@@ -141,36 +146,48 @@ module Mocha
141
146
  # object.stubs(:stubbed_method_one).returns(:result_one)
142
147
  # object.stubs(:stubbed_method_two).returns(:result_two)
143
148
  def stubs(method_name_or_hash, backtrace = nil)
149
+ expectation = nil
144
150
  iterator = ArgumentIterator.new(method_name_or_hash)
145
- iterator.each { |*args|
151
+ iterator.each do |*args|
146
152
  method_name = args.shift
147
153
  ensure_method_not_already_defined(method_name)
148
154
  expectation = Expectation.new(self, method_name, backtrace)
149
155
  expectation.at_least(0)
150
- expectation.returns(args.shift) if args.length > 0
156
+ expectation.returns(args.shift) unless args.empty?
151
157
  @expectations.add(expectation)
152
- }
158
+ end
159
+ expectation
153
160
  end
154
161
 
155
- # 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.
156
163
  #
157
- # @param [Symbol] method_name name of method to unstub.
164
+ # @param [Array<Symbol>] method_names names of methods to unstub.
158
165
  #
159
166
  # @example Invoking an unstubbed method causes error to be raised
160
- # object = mock('mock') do
167
+ # object = mock('mock')
161
168
  # object.stubs(:stubbed_method).returns(:result_one)
162
169
  # object.stubbed_method # => :result_one
163
170
  # object.unstub(:stubbed_method)
164
171
  # object.stubbed_method # => unexpected invocation: #<Mock:mock>.stubbed_method()
165
- def unstub(method_name)
166
- @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
167
184
  end
168
185
 
169
- # 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.
170
187
  #
171
- # 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).
172
189
  #
173
- # 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.
174
191
  #
175
192
  # Note that the methods on +responder+ are never actually invoked.
176
193
  #
@@ -220,11 +237,11 @@ module Mocha
220
237
  self
221
238
  end
222
239
 
223
- # 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+.
224
241
  #
225
- # 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).
226
243
  #
227
- # 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.
228
245
  #
229
246
  # Note that the methods on the responder instance are never actually invoked.
230
247
  #
@@ -253,7 +270,7 @@ module Mocha
253
270
  end
254
271
 
255
272
  # @private
256
- def initialize(mockery, name = nil, receiver = nil, &block)
273
+ def initialize(mockery, name = nil, receiver = nil)
257
274
  @mockery = mockery
258
275
  @name = name || DefaultName.new(self)
259
276
  @receiver = receiver || DefaultReceiver.new(self)
@@ -261,10 +278,7 @@ module Mocha
261
278
  @everything_stubbed = false
262
279
  @responder = nil
263
280
  @unexpected_invocation = nil
264
- if block
265
- Deprecation.warning('Passing a block is deprecated. Use Object#tap or define stubs/expectations with an explicit receiver instead.')
266
- instance_eval(&block)
267
- end
281
+ @expired = false
268
282
  end
269
283
 
270
284
  # @private
@@ -274,6 +288,8 @@ module Mocha
274
288
 
275
289
  alias_method :__stubs__, :stubs
276
290
 
291
+ alias_method :__singleton_class__, :singleton_class
292
+
277
293
  alias_method :quacks_like, :responds_like
278
294
  alias_method :quacks_like_instance_of, :responds_like_instance_of
279
295
 
@@ -293,44 +309,31 @@ module Mocha
293
309
  end
294
310
 
295
311
  # @private
312
+ # rubocop:disable Style/MethodMissingSuper
296
313
  def method_missing(symbol, *arguments, &block)
297
- if @responder and not @responder.respond_to?(symbol)
298
- raise NoMethodError, "undefined method `#{symbol}' for #{self.mocha_inspect} which responds like #{@responder.mocha_inspect}"
299
- end
300
- if matching_expectation_allowing_invocation = all_expectations.match_allowing_invocation(symbol, *arguments)
301
- matching_expectation_allowing_invocation.invoke(&block)
302
- else
303
- if (matching_expectation = all_expectations.match(symbol, *arguments)) || (!matching_expectation && !@everything_stubbed)
304
- if @unexpected_invocation.nil?
305
- @unexpected_invocation = UnexpectedInvocation.new(self, symbol, *arguments)
306
- matching_expectation.invoke(&block) if matching_expectation
307
- message = @unexpected_invocation.full_description
308
- message << @mockery.mocha_inspect
309
- else
310
- message = @unexpected_invocation.short_description
311
- end
312
- raise ExpectationErrorFactory.build(message, caller)
313
- end
314
- end
314
+ handle_method_call(symbol, arguments, block)
315
315
  end
316
+ ruby2_keywords(:method_missing)
317
+ # rubocop:enable Style/MethodMissingSuper
316
318
 
317
319
  # @private
318
- def respond_to_missing?(symbol, include_private = false)
319
- if @responder then
320
- if @responder.method(:respond_to?).arity > 1
321
- @responder.respond_to?(symbol, include_private)
322
- else
323
- @responder.respond_to?(symbol)
324
- end
325
- else
326
- @everything_stubbed || all_expectations.matches_method?(symbol)
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)
327
328
  end
328
329
  end
329
330
 
330
331
  # @private
331
- if PRE_RUBY_V19
332
- def respond_to?(symbol, include_private = false)
333
- respond_to_missing?(symbol, include_private)
332
+ def respond_to_missing?(symbol, _include_all)
333
+ if @responder
334
+ @responder.respond_to?(symbol)
335
+ else
336
+ @everything_stubbed || all_expectations.matches_method?(symbol)
334
337
  end
335
338
  end
336
339
 
@@ -339,6 +342,11 @@ module Mocha
339
342
  @expectations.verified?(assertion_counter)
340
343
  end
341
344
 
345
+ # @private
346
+ def __expire__
347
+ @expired = true
348
+ end
349
+
342
350
  # @private
343
351
  def mocha_inspect
344
352
  @name.mocha_inspect
@@ -351,7 +359,7 @@ module Mocha
351
359
 
352
360
  # @private
353
361
  def ensure_method_not_already_defined(method_name)
354
- 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)
355
363
  end
356
364
 
357
365
  # @private
@@ -359,6 +367,34 @@ module Mocha
359
367
  @expectations.any?
360
368
  end
361
369
 
362
- 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
363
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
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
364
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('%<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