mocha 0.10.5 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (262) hide show
  1. checksums.yaml +7 -0
  2. data/.github/FUNDING.yml +1 -0
  3. data/.rubocop.yml +61 -0
  4. data/.rubocop_todo.yml +27 -0
  5. data/.yardopts +24 -0
  6. data/CONTRIBUTING.md +7 -0
  7. data/COPYING.md +3 -0
  8. data/Gemfile +2 -2
  9. data/{MIT-LICENSE.rdoc → MIT-LICENSE.md} +1 -1
  10. data/README.md +363 -0
  11. data/{RELEASE.rdoc → RELEASE.md} +436 -35
  12. data/Rakefile +87 -87
  13. data/gemfiles/Gemfile.minitest.latest +2 -2
  14. data/gemfiles/Gemfile.test-unit.latest +6 -2
  15. data/init.rb +1 -3
  16. data/lib/mocha/any_instance_method.rb +12 -45
  17. data/lib/mocha/api.rb +199 -131
  18. data/lib/mocha/argument_iterator.rb +6 -10
  19. data/lib/mocha/backtrace_filter.rb +1 -5
  20. data/lib/mocha/block_matcher.rb +31 -0
  21. data/lib/mocha/cardinality.rb +77 -66
  22. data/lib/mocha/central.rb +27 -18
  23. data/lib/mocha/change_state_side_effect.rb +3 -7
  24. data/lib/mocha/class_methods.rb +62 -0
  25. data/lib/mocha/configuration.rb +399 -46
  26. data/lib/mocha/debug.rb +12 -0
  27. data/lib/mocha/deprecation.rb +11 -12
  28. data/lib/mocha/detection/mini_test.rb +23 -0
  29. data/lib/mocha/detection/test_unit.rb +27 -0
  30. data/lib/mocha/error_with_filtered_backtrace.rb +13 -0
  31. data/lib/mocha/exception_raiser.rb +8 -10
  32. data/lib/mocha/expectation.rb +290 -151
  33. data/lib/mocha/expectation_error.rb +6 -13
  34. data/lib/mocha/expectation_error_factory.rb +35 -0
  35. data/lib/mocha/expectation_list.rb +22 -22
  36. data/lib/mocha/hooks.rb +42 -0
  37. data/lib/mocha/in_state_ordering_constraint.rb +3 -7
  38. data/lib/mocha/inspect.rb +35 -43
  39. data/lib/mocha/instance_method.rb +12 -21
  40. data/lib/mocha/integration/assertion_counter.rb +13 -0
  41. data/lib/mocha/integration/mini_test/adapter.rb +52 -0
  42. data/lib/mocha/integration/mini_test/exception_translation.rb +1 -7
  43. data/lib/mocha/integration/mini_test/nothing.rb +19 -0
  44. data/lib/mocha/integration/mini_test/version_13.rb +35 -25
  45. data/lib/mocha/integration/mini_test/version_140.rb +35 -26
  46. data/lib/mocha/integration/mini_test/version_141.rb +43 -34
  47. data/lib/mocha/integration/mini_test/version_142_to_172.rb +44 -35
  48. data/lib/mocha/integration/mini_test/version_200.rb +45 -36
  49. data/lib/mocha/integration/mini_test/version_201_to_222.rb +44 -35
  50. data/lib/mocha/integration/mini_test/version_2110_to_2111.rb +70 -0
  51. data/lib/mocha/integration/mini_test/version_2112_to_320.rb +73 -0
  52. data/lib/mocha/integration/mini_test/version_230_to_2101.rb +68 -0
  53. data/lib/mocha/integration/mini_test.rb +51 -49
  54. data/lib/mocha/integration/monkey_patcher.rb +24 -0
  55. data/lib/mocha/integration/test_unit/adapter.rb +50 -0
  56. data/lib/mocha/integration/test_unit/gem_version_200.rb +39 -29
  57. data/lib/mocha/integration/test_unit/gem_version_201_to_202.rb +39 -29
  58. data/lib/mocha/integration/test_unit/gem_version_203_to_220.rb +39 -29
  59. data/lib/mocha/integration/test_unit/gem_version_230_to_250.rb +68 -0
  60. data/lib/mocha/integration/test_unit/nothing.rb +19 -0
  61. data/lib/mocha/integration/test_unit/ruby_version_185_and_below.rb +39 -29
  62. data/lib/mocha/integration/test_unit/ruby_version_186_and_above.rb +40 -30
  63. data/lib/mocha/integration/test_unit.rb +45 -51
  64. data/lib/mocha/integration.rb +6 -33
  65. data/lib/mocha/invocation.rb +77 -0
  66. data/lib/mocha/is_a.rb +0 -2
  67. data/lib/mocha/logger.rb +2 -6
  68. data/lib/mocha/macos_version.rb +5 -0
  69. data/lib/mocha/method_matcher.rb +6 -10
  70. data/lib/mocha/minitest.rb +8 -0
  71. data/lib/mocha/mock.rb +266 -79
  72. data/lib/mocha/mockery.rb +104 -106
  73. data/lib/mocha/names.rb +10 -20
  74. data/lib/mocha/not_initialized_error.rb +7 -0
  75. data/lib/mocha/object_methods.rb +169 -0
  76. data/lib/mocha/parameter_matchers/all_of.rb +18 -14
  77. data/lib/mocha/parameter_matchers/any_of.rb +19 -14
  78. data/lib/mocha/parameter_matchers/any_parameters.rb +14 -13
  79. data/lib/mocha/parameter_matchers/anything.rb +17 -14
  80. data/lib/mocha/parameter_matchers/base.rb +33 -31
  81. data/lib/mocha/parameter_matchers/equals.rb +18 -13
  82. data/lib/mocha/parameter_matchers/equivalent_uri.rb +58 -0
  83. data/lib/mocha/parameter_matchers/has_entries.rb +19 -14
  84. data/lib/mocha/parameter_matchers/has_entry.rb +58 -26
  85. data/lib/mocha/parameter_matchers/has_key.rb +18 -13
  86. data/lib/mocha/parameter_matchers/has_keys.rb +53 -0
  87. data/lib/mocha/parameter_matchers/has_value.rb +18 -13
  88. data/lib/mocha/parameter_matchers/includes.rb +80 -19
  89. data/lib/mocha/parameter_matchers/instance_methods.rb +18 -0
  90. data/lib/mocha/parameter_matchers/instance_of.rb +18 -13
  91. data/lib/mocha/parameter_matchers/is_a.rb +20 -14
  92. data/lib/mocha/parameter_matchers/kind_of.rb +20 -13
  93. data/lib/mocha/parameter_matchers/not.rb +19 -14
  94. data/lib/mocha/parameter_matchers/optionally.rb +23 -17
  95. data/lib/mocha/parameter_matchers/regexp_matches.rb +16 -12
  96. data/lib/mocha/parameter_matchers/responds_with.rb +17 -11
  97. data/lib/mocha/parameter_matchers/yaml_equivalent.rb +15 -9
  98. data/lib/mocha/parameter_matchers.rb +4 -5
  99. data/lib/mocha/parameters_matcher.rb +11 -14
  100. data/lib/mocha/raised_exception.rb +11 -0
  101. data/lib/mocha/receivers.rb +45 -0
  102. data/lib/mocha/return_values.rb +11 -15
  103. data/lib/mocha/ruby_version.rb +4 -0
  104. data/lib/mocha/sequence.rb +21 -17
  105. data/lib/mocha/setup.rb +14 -0
  106. data/lib/mocha/single_return_value.rb +5 -8
  107. data/lib/mocha/singleton_class.rb +9 -0
  108. data/lib/mocha/state_machine.rb +69 -67
  109. data/lib/mocha/stubbed_method.rb +125 -0
  110. data/lib/mocha/stubbing_error.rb +6 -14
  111. data/lib/mocha/test_unit.rb +8 -0
  112. data/lib/mocha/thrower.rb +6 -8
  113. data/lib/mocha/thrown_object.rb +12 -0
  114. data/lib/mocha/version.rb +1 -1
  115. data/lib/mocha/yield_parameters.rb +12 -22
  116. data/lib/mocha.rb +8 -3
  117. data/mocha.gemspec +43 -34
  118. data/yard-templates/default/layout/html/google_analytics.erb +8 -0
  119. data/yard-templates/default/layout/html/setup.rb +5 -0
  120. metadata +123 -268
  121. data/COPYING.rdoc +0 -3
  122. data/README.rdoc +0 -54
  123. data/examples/misc.rb +0 -43
  124. data/examples/mocha.rb +0 -25
  125. data/examples/stubba.rb +0 -64
  126. data/gemfiles/Gemfile.minitest.1.3.0 +0 -7
  127. data/gemfiles/Gemfile.minitest.1.4.0 +0 -7
  128. data/gemfiles/Gemfile.minitest.1.4.1 +0 -7
  129. data/gemfiles/Gemfile.minitest.1.4.2 +0 -7
  130. data/gemfiles/Gemfile.minitest.2.0.0 +0 -7
  131. data/gemfiles/Gemfile.minitest.2.0.1 +0 -7
  132. data/gemfiles/Gemfile.minitest.2.3.0 +0 -7
  133. data/gemfiles/Gemfile.test-unit.2.0.0 +0 -8
  134. data/gemfiles/Gemfile.test-unit.2.0.1 +0 -7
  135. data/gemfiles/Gemfile.test-unit.2.0.3 +0 -7
  136. data/lib/mocha/class_method.rb +0 -98
  137. data/lib/mocha/integration/mini_test/assertion_counter.rb +0 -23
  138. data/lib/mocha/integration/mini_test/version_230_to_262.rb +0 -59
  139. data/lib/mocha/integration/test_unit/assertion_counter.rb +0 -23
  140. data/lib/mocha/integration/test_unit/gem_version_230_to_240.rb +0 -58
  141. data/lib/mocha/module_method.rb +0 -16
  142. data/lib/mocha/multiple_yields.rb +0 -20
  143. data/lib/mocha/no_yields.rb +0 -11
  144. data/lib/mocha/object.rb +0 -223
  145. data/lib/mocha/options.rb +0 -1
  146. data/lib/mocha/parameter_matchers/object.rb +0 -15
  147. data/lib/mocha/parameter_matchers/query_string.rb +0 -47
  148. data/lib/mocha/pretty_parameters.rb +0 -28
  149. data/lib/mocha/single_yield.rb +0 -18
  150. data/lib/mocha/standalone.rb +0 -1
  151. data/lib/mocha/unexpected_invocation.rb +0 -18
  152. data/lib/mocha_standalone.rb +0 -2
  153. data/lib/stubba.rb +0 -4
  154. data/test/acceptance/acceptance_test_helper.rb +0 -41
  155. data/test/acceptance/api_test.rb +0 -139
  156. data/test/acceptance/bug_18914_test.rb +0 -43
  157. data/test/acceptance/bug_21465_test.rb +0 -34
  158. data/test/acceptance/bug_21563_test.rb +0 -25
  159. data/test/acceptance/exception_rescue_test.rb +0 -55
  160. data/test/acceptance/expectations_on_multiple_methods_test.rb +0 -55
  161. data/test/acceptance/expected_invocation_count_test.rb +0 -232
  162. data/test/acceptance/failure_messages_test.rb +0 -64
  163. data/test/acceptance/issue_65_test.rb +0 -63
  164. data/test/acceptance/issue_70_test.rb +0 -55
  165. data/test/acceptance/minitest_test.rb +0 -162
  166. data/test/acceptance/mocha_example_test.rb +0 -98
  167. data/test/acceptance/mocha_test_result_test.rb +0 -84
  168. data/test/acceptance/mock_test.rb +0 -100
  169. data/test/acceptance/mock_with_initializer_block_test.rb +0 -51
  170. data/test/acceptance/mocked_methods_dispatch_test.rb +0 -78
  171. data/test/acceptance/multiple_expectations_failure_message_test.rb +0 -68
  172. data/test/acceptance/optional_parameters_test.rb +0 -70
  173. data/test/acceptance/parameter_matcher_test.rb +0 -300
  174. data/test/acceptance/partial_mocks_test.rb +0 -47
  175. data/test/acceptance/raise_exception_test.rb +0 -39
  176. data/test/acceptance/return_value_test.rb +0 -52
  177. data/test/acceptance/sequence_test.rb +0 -192
  178. data/test/acceptance/states_test.rb +0 -70
  179. data/test/acceptance/stub_any_instance_method_test.rb +0 -198
  180. data/test/acceptance/stub_class_method_defined_on_active_record_association_proxy_test.rb +0 -106
  181. data/test/acceptance/stub_class_method_defined_on_class_test.rb +0 -72
  182. data/test/acceptance/stub_class_method_defined_on_module_test.rb +0 -75
  183. data/test/acceptance/stub_class_method_defined_on_superclass_test.rb +0 -75
  184. data/test/acceptance/stub_everything_test.rb +0 -56
  185. data/test/acceptance/stub_instance_method_defined_on_active_record_association_proxy_test.rb +0 -93
  186. data/test/acceptance/stub_instance_method_defined_on_class_and_aliased_test.rb +0 -69
  187. data/test/acceptance/stub_instance_method_defined_on_class_test.rb +0 -66
  188. data/test/acceptance/stub_instance_method_defined_on_kernel_module_test.rb +0 -75
  189. data/test/acceptance/stub_instance_method_defined_on_module_test.rb +0 -75
  190. data/test/acceptance/stub_instance_method_defined_on_object_class_test.rb +0 -75
  191. data/test/acceptance/stub_instance_method_defined_on_singleton_class_test.rb +0 -70
  192. data/test/acceptance/stub_instance_method_defined_on_superclass_test.rb +0 -72
  193. data/test/acceptance/stub_module_method_test.rb +0 -163
  194. data/test/acceptance/stub_test.rb +0 -52
  195. data/test/acceptance/stubba_example_test.rb +0 -102
  196. data/test/acceptance/stubba_test.rb +0 -15
  197. data/test/acceptance/stubba_test_result_test.rb +0 -66
  198. data/test/acceptance/stubbing_error_backtrace_test.rb +0 -64
  199. data/test/acceptance/stubbing_method_unnecessarily_test.rb +0 -65
  200. data/test/acceptance/stubbing_non_existent_any_instance_method_test.rb +0 -130
  201. data/test/acceptance/stubbing_non_existent_class_method_test.rb +0 -157
  202. data/test/acceptance/stubbing_non_existent_instance_method_test.rb +0 -147
  203. data/test/acceptance/stubbing_non_public_any_instance_method_test.rb +0 -130
  204. data/test/acceptance/stubbing_non_public_class_method_test.rb +0 -163
  205. data/test/acceptance/stubbing_non_public_instance_method_test.rb +0 -143
  206. data/test/acceptance/stubbing_on_non_mock_object_test.rb +0 -64
  207. data/test/acceptance/throw_test.rb +0 -45
  208. data/test/acceptance/unstubbing_test.rb +0 -151
  209. data/test/deprecation_disabler.rb +0 -15
  210. data/test/execution_point.rb +0 -36
  211. data/test/method_definer.rb +0 -24
  212. data/test/mini_test_result.rb +0 -83
  213. data/test/simple_counter.rb +0 -13
  214. data/test/test_helper.rb +0 -11
  215. data/test/test_runner.rb +0 -50
  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 -260
  223. data/test/unit/configuration_test.rb +0 -38
  224. data/test/unit/date_time_inspect_test.rb +0 -21
  225. data/test/unit/exception_raiser_test.rb +0 -42
  226. data/test/unit/expectation_list_test.rb +0 -71
  227. data/test/unit/expectation_test.rb +0 -480
  228. data/test/unit/hash_inspect_test.rb +0 -16
  229. data/test/unit/in_state_ordering_constraint_test.rb +0 -43
  230. data/test/unit/method_matcher_test.rb +0 -23
  231. data/test/unit/mock_test.rb +0 -312
  232. data/test/unit/mockery_test.rb +0 -150
  233. data/test/unit/multiple_yields_test.rb +0 -18
  234. data/test/unit/no_yields_test.rb +0 -18
  235. data/test/unit/object_inspect_test.rb +0 -38
  236. data/test/unit/object_test.rb +0 -87
  237. data/test/unit/parameter_matchers/all_of_test.rb +0 -26
  238. data/test/unit/parameter_matchers/any_of_test.rb +0 -26
  239. data/test/unit/parameter_matchers/anything_test.rb +0 -21
  240. data/test/unit/parameter_matchers/equals_test.rb +0 -25
  241. data/test/unit/parameter_matchers/has_entries_test.rb +0 -51
  242. data/test/unit/parameter_matchers/has_entry_test.rb +0 -96
  243. data/test/unit/parameter_matchers/has_key_test.rb +0 -55
  244. data/test/unit/parameter_matchers/has_value_test.rb +0 -57
  245. data/test/unit/parameter_matchers/includes_test.rb +0 -44
  246. data/test/unit/parameter_matchers/instance_of_test.rb +0 -25
  247. data/test/unit/parameter_matchers/is_a_test.rb +0 -25
  248. data/test/unit/parameter_matchers/kind_of_test.rb +0 -25
  249. data/test/unit/parameter_matchers/not_test.rb +0 -26
  250. data/test/unit/parameter_matchers/regexp_matches_test.rb +0 -46
  251. data/test/unit/parameter_matchers/responds_with_test.rb +0 -25
  252. data/test/unit/parameter_matchers/stub_matcher.rb +0 -27
  253. data/test/unit/parameter_matchers/yaml_equivalent_test.rb +0 -25
  254. data/test/unit/parameters_matcher_test.rb +0 -121
  255. data/test/unit/return_values_test.rb +0 -63
  256. data/test/unit/sequence_test.rb +0 -104
  257. data/test/unit/single_return_value_test.rb +0 -14
  258. data/test/unit/single_yield_test.rb +0 -18
  259. data/test/unit/state_machine_test.rb +0 -98
  260. data/test/unit/string_inspect_test.rb +0 -11
  261. data/test/unit/thrower_test.rb +0 -20
  262. data/test/unit/yield_parameters_test.rb +0 -93
data/lib/mocha/mock.rb CHANGED
@@ -1,103 +1,198 @@
1
- require 'metaclass'
1
+ require 'mocha/singleton_class'
2
2
  require 'mocha/expectation'
3
3
  require 'mocha/expectation_list'
4
+ require 'mocha/invocation'
4
5
  require 'mocha/names'
6
+ require 'mocha/receivers'
5
7
  require 'mocha/method_matcher'
6
8
  require 'mocha/parameters_matcher'
7
- require 'mocha/unexpected_invocation'
8
9
  require 'mocha/argument_iterator'
10
+ require 'mocha/expectation_error_factory'
11
+ require 'mocha/ruby_version'
9
12
 
10
- module Mocha # :nodoc:
11
-
13
+ module Mocha
12
14
  # Traditional mock object.
13
15
  #
14
- # Methods return an Expectation which can be further modified by methods on Expectation.
16
+ # {expects} and {stubs} return an {Expectation} which can be further modified
17
+ # by methods on {Expectation}.
18
+ #
19
+ # {responds_like} and {responds_like_instance_of} both return a {Mock}, and
20
+ # can therefore, be chained to the original creation methods in {API}.
21
+ # They force the mock to indicate what it is supposed to be mocking, thus
22
+ # making it a safer verifying mock. They check that the underlying +responder+
23
+ # will actually respond to the methods being stubbed, throwing a
24
+ # +NoMethodError+ upon invocation otherwise.
25
+ #
26
+ # Stubs and expectations are basically the same thing. A stub is just an
27
+ # expectation of zero or more invocations. The {#stubs} method is syntactic
28
+ # sugar to make the intent of the test more explicit.
29
+ #
30
+ # When a method is invoked on a mock object, the mock object searches through
31
+ # its expectations from newest to oldest to find one that matches the
32
+ # invocation. After the invocation, the matching expectation might stop
33
+ # matching further invocations. For example, an +expects(:foo).once+
34
+ # expectation only matches once and will be ignored on future invocations
35
+ # while an +expects(:foo).at_least_once+ expectation will always be matched
36
+ # against invocations.
37
+ #
38
+ # This scheme allows you to:
39
+ #
40
+ # - Set up default stubs in your the +setup+ method of your test class and
41
+ # override some of those stubs in individual tests.
42
+ # - Set up different +once+ expectations for the same method with different
43
+ # action per invocation. However, it's better to use the
44
+ # {Expectation#returns} method with multiple arguments to do this, as
45
+ # described below.
46
+ #
47
+ # However, there are some possible "gotchas" caused by this scheme:
48
+ #
49
+ # - if you create an expectation and then a stub for the same method, the
50
+ # stub will always override the expectation and the expectation will never
51
+ # be met.
52
+ # - if you create a stub and then an expectation for the same method, the
53
+ # expectation will match, and when it stops matching the stub will be used
54
+ # instead, possibly masking test failures.
55
+ # - if you create different expectations for the same method, they will be
56
+ # invoked in the opposite order than that in which they were specified,
57
+ # rather than the same order.
58
+ #
59
+ # The best thing to do is not set up multiple expectations and stubs for the
60
+ # same method with exactly the same matchers. Instead, use the
61
+ # {Expectation#returns} method with multiple arguments to create multiple
62
+ # actions for a method. You can also chain multiple calls to
63
+ # {Expectation#returns} and {Expectation#raises} (along with syntactic sugar
64
+ # {Expectation#then} if desired).
65
+ #
66
+ # @example
67
+ # object = mock()
68
+ # object.stubs(:expected_method).returns(1, 2).then.raises(Exception)
69
+ # object.expected_method # => 1
70
+ # object.expected_method # => 2
71
+ # object.expected_method # => raises exception of class Exception1
72
+ #
73
+ # If you want to specify more complex ordering or order invocations across
74
+ # different mock objects, use the {Expectation#in_sequence} method to
75
+ # explicitly define a total or partial ordering of invocations.
15
76
  class Mock
16
-
17
- # :call-seq: expects(method_name) -> expectation
18
- # expects(method_names_vs_return_values) -> last expectation
77
+ # Adds an expectation that the specified method must be called exactly once with any parameters.
19
78
  #
20
- # Adds an expectation that a method identified by +method_name+ Symbol/String must be called exactly once with any parameters.
21
- # Returns the new expectation which can be further modified by methods on Expectation.
22
- # object = mock()
23
- # object.expects(:method1)
24
- # object.method1
25
- # # no error raised
79
+ # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
26
80
  #
81
+ # @overload def expects(method_name)
82
+ # @param [Symbol,String] method_name name of expected method
83
+ # @overload def expects(expected_methods_vs_return_values)
84
+ # @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.
85
+ #
86
+ # @example Expected method invoked once so no error raised
27
87
  # object = mock()
28
- # object.expects(:method1)
29
- # # error raised, because method1 not called exactly once
30
- # If +method_names_vs_return_values+ is a +Hash+, an expectation will be set up for each entry using the key as +method_name+ and value as +return_value+.
88
+ # object.expects(:expected_method)
89
+ # object.expected_method
90
+ #
91
+ # @example Expected method not invoked so error raised
31
92
  # object = mock()
32
- # object.expects(:method1 => :result1, :method2 => :result2)
93
+ # object.expects(:expected_method)
94
+ # # error raised when test completes, because expected_method not called exactly once
33
95
  #
34
- # # exactly equivalent to
96
+ # @example Expected method invoked twice so error raised
97
+ # object = mock()
98
+ # object.expects(:expected_method)
99
+ # object.expected_method
100
+ # object.expected_method # => error raised when expected method invoked second time
35
101
  #
102
+ # @example Setup multiple expectations using +expected_methods_vs_return_values+.
36
103
  # object = mock()
37
- # object.expects(:method1).returns(:result1)
38
- # object.expects(:method2).returns(:result2)
104
+ # object.expects(:expected_method_one => :result_one, :expected_method_two => :result_two)
105
+ #
106
+ # # is exactly equivalent to
39
107
  #
40
- # Aliased by <tt>\_\_expects\_\_</tt>
108
+ # object = mock()
109
+ # object.expects(:expected_method_one).returns(:result_one)
110
+ # object.expects(:expected_method_two).returns(:result_two)
41
111
  def expects(method_name_or_hash, backtrace = nil)
42
112
  iterator = ArgumentIterator.new(method_name_or_hash)
43
- iterator.each { |*args|
113
+ iterator.each do |*args|
44
114
  method_name = args.shift
45
115
  ensure_method_not_already_defined(method_name)
46
116
  expectation = Expectation.new(self, method_name, backtrace)
47
- expectation.returns(args.shift) if args.length > 0
117
+ expectation.returns(args.shift) unless args.empty?
48
118
  @expectations.add(expectation)
49
- }
119
+ end
50
120
  end
51
121
 
52
- # :call-seq: stubs(method_name) -> expectation
53
- # stubs(method_names_vs_return_values) -> last expectation
122
+ # Adds an expectation that the specified method may be called any number of times with any parameters.
123
+ #
124
+ # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
54
125
  #
55
- # Adds an expectation that a method identified by +method_name+ Symbol/String may be called any number of times with any parameters.
56
- # Returns the new expectation which can be further modified by methods on Expectation.
126
+ # @overload def stubs(method_name)
127
+ # @param [Symbol,String] method_name name of stubbed method
128
+ # @overload def stubs(stubbed_methods_vs_return_values)
129
+ # @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.
130
+ #
131
+ # @example No error raised however many times stubbed method is invoked
57
132
  # object = mock()
58
- # object.stubs(:method1)
59
- # object.method1
60
- # object.method1
133
+ # object.stubs(:stubbed_method)
134
+ # object.stubbed_method
135
+ # object.stubbed_method
61
136
  # # no error raised
62
- # If +method_names_vs_return_values+ is a +Hash+, an expectation will be set up for each entry using the key as +method_name+ and value as +return_value+.
137
+ #
138
+ # @example Setup multiple expectations using +stubbed_methods_vs_return_values+.
63
139
  # object = mock()
64
- # object.stubs(:method1 => :result1, :method2 => :result2)
140
+ # object.stubs(:stubbed_method_one => :result_one, :stubbed_method_two => :result_two)
65
141
  #
66
- # # exactly equivalent to
142
+ # # is exactly equivalent to
67
143
  #
68
144
  # object = mock()
69
- # object.stubs(:method1).returns(:result1)
70
- # object.stubs(:method2).returns(:result2)
71
- #
72
- # Aliased by <tt>\_\_stubs\_\_</tt>
145
+ # object.stubs(:stubbed_method_one).returns(:result_one)
146
+ # object.stubs(:stubbed_method_two).returns(:result_two)
73
147
  def stubs(method_name_or_hash, backtrace = nil)
74
148
  iterator = ArgumentIterator.new(method_name_or_hash)
75
- iterator.each { |*args|
149
+ iterator.each do |*args|
76
150
  method_name = args.shift
77
151
  ensure_method_not_already_defined(method_name)
78
152
  expectation = Expectation.new(self, method_name, backtrace)
79
153
  expectation.at_least(0)
80
- expectation.returns(args.shift) if args.length > 0
154
+ expectation.returns(args.shift) unless args.empty?
81
155
  @expectations.add(expectation)
82
- }
156
+ end
83
157
  end
84
158
 
85
- def unstub(method_name)
86
- @expectations.remove_all_matching_method(method_name)
159
+ # Removes the specified stubbed methods (added by calls to {#expects} or {#stubs}) and all expectations associated with them.
160
+ #
161
+ # @param [Array<Symbol>] method_names names of methods to unstub.
162
+ #
163
+ # @example Invoking an unstubbed method causes error to be raised
164
+ # object = mock('mock') do
165
+ # object.stubs(:stubbed_method).returns(:result_one)
166
+ # object.stubbed_method # => :result_one
167
+ # object.unstub(:stubbed_method)
168
+ # object.stubbed_method # => unexpected invocation: #<Mock:mock>.stubbed_method()
169
+ #
170
+ # @example Unstubbing multiple methods.
171
+ # multiplier.unstub(:double, :triple)
172
+ #
173
+ # # exactly equivalent to
174
+ #
175
+ # multiplier.unstub(:double)
176
+ # multiplier.unstub(:triple)
177
+ def unstub(*method_names)
178
+ method_names.each do |method_name|
179
+ @expectations.remove_all_matching_method(method_name)
180
+ end
87
181
  end
88
182
 
89
- # :call-seq: responds_like(responder) -> mock
183
+ # 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.
90
184
  #
91
- # Constrains the +mock+ so that it can only expect or stub methods to which +responder+ responds. The constraint is only applied at method invocation time.
185
+ # A +NoMethodError+ will be raised if the +responder+ does not +#respond_to?+ a method invocation (even if the method has been expected or stubbed).
92
186
  #
93
- # A +NoMethodError+ will be raised if the +responder+ does not <tt>respond_to?</tt> a method invocation (even if the method has been expected or stubbed).
187
+ # The {Mock} instance will delegate its +#respond_to?+ method to the +responder+.
94
188
  #
95
- # The +mock+ will delegate its <tt>respond_to?</tt> method to the +responder+.
96
- # class Sheep
97
- # def chew(grass); end
98
- # def self.number_of_legs; end
99
- # end
189
+ # Note that the methods on +responder+ are never actually invoked.
100
190
  #
191
+ # @param [Object, #respond_to?] responder an object used to determine whether {Mock} instance should +#respond_to?+ to an invocation.
192
+ # @return [Mock] the same {Mock} instance, thereby allowing invocations of other {Mock} methods to be chained.
193
+ # @see #responds_like_instance_of
194
+ #
195
+ # @example Normal mocking
101
196
  # sheep = mock('sheep')
102
197
  # sheep.expects(:chew)
103
198
  # sheep.expects(:foo)
@@ -107,6 +202,11 @@ module Mocha # :nodoc:
107
202
  # sheep.foo
108
203
  # # no error raised
109
204
  #
205
+ # @example Using {#responds_like} with an instance method
206
+ # class Sheep
207
+ # def chew(grass); end
208
+ # end
209
+ #
110
210
  # sheep = mock('sheep')
111
211
  # sheep.responds_like(Sheep.new)
112
212
  # sheep.expects(:chew)
@@ -116,98 +216,185 @@ module Mocha # :nodoc:
116
216
  # sheep.chew
117
217
  # sheep.foo # => raises NoMethodError exception
118
218
  #
219
+ # @example Using {#responds_like} with a class method
220
+ # class Sheep
221
+ # def self.number_of_legs; end
222
+ # end
223
+ #
119
224
  # sheep_class = mock('sheep_class')
120
225
  # sheep_class.responds_like(Sheep)
121
226
  # sheep_class.stubs(:number_of_legs).returns(4)
122
227
  # sheep_class.expects(:foo)
123
228
  # sheep_class.respond_to?(:number_of_legs) # => true
124
229
  # sheep_class.respond_to?(:foo) # => false
125
- # assert_equal 4, sheep_class.number_of_legs
230
+ # sheep_class.number_of_legs # => 4
126
231
  # sheep_class.foo # => raises NoMethodError exception
127
- #
128
- # Aliased by +quacks_like+
129
- def responds_like(object)
130
- @responder = object
232
+ def responds_like(responder)
233
+ @responder = responder
131
234
  self
132
235
  end
133
236
 
134
- # :stopdoc:
237
+ # 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+.
238
+ #
239
+ # 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).
240
+ #
241
+ # The {Mock} instance will delegate its +#respond_to?+ method to the responder instance.
242
+ #
243
+ # Note that the methods on the responder instance are never actually invoked.
244
+ #
245
+ # @param [Class] responder_class a class used to determine whether {Mock} instance should +#respond_to?+ to an invocation.
246
+ # @return [Mock] the same {Mock} instance, thereby allowing invocations of other {Mock} methods to be chained.
247
+ # @see #responds_like
248
+ #
249
+ # @example Using {#responds_like_instance_of}
250
+ # class Sheep
251
+ # def initialize
252
+ # raise "some awkward code we don't want to call"
253
+ # end
254
+ # def chew(grass); end
255
+ # end
256
+ #
257
+ # sheep = mock('sheep')
258
+ # sheep.responds_like_instance_of(Sheep)
259
+ # sheep.expects(:chew)
260
+ # sheep.expects(:foo)
261
+ # sheep.respond_to?(:chew) # => true
262
+ # sheep.respond_to?(:foo) # => false
263
+ # sheep.chew
264
+ # sheep.foo # => raises NoMethodError exception
265
+ def responds_like_instance_of(responder_class)
266
+ responds_like(responder_class.allocate)
267
+ end
135
268
 
136
- def initialize(mockery, name = nil, &block)
269
+ # @private
270
+ def initialize(mockery, name = nil, receiver = nil)
137
271
  @mockery = mockery
138
272
  @name = name || DefaultName.new(self)
273
+ @receiver = receiver || DefaultReceiver.new(self)
139
274
  @expectations = ExpectationList.new
140
275
  @everything_stubbed = false
141
276
  @responder = nil
142
- instance_eval(&block) if block
277
+ @unexpected_invocation = nil
278
+ @expired = false
143
279
  end
144
280
 
281
+ # @private
145
282
  attr_reader :everything_stubbed
146
283
 
147
284
  alias_method :__expects__, :expects
148
285
 
149
286
  alias_method :__stubs__, :stubs
150
287
 
288
+ alias_method :__singleton_class__, :singleton_class
289
+
151
290
  alias_method :quacks_like, :responds_like
291
+ alias_method :quacks_like_instance_of, :responds_like_instance_of
152
292
 
293
+ # @private
153
294
  def __expectations__
154
295
  @expectations
155
296
  end
156
297
 
298
+ # @private
157
299
  def stub_everything
158
300
  @everything_stubbed = true
159
301
  end
160
302
 
161
- def method_missing(symbol, *arguments, &block)
162
- if @responder and not @responder.respond_to?(symbol)
163
- raise NoMethodError, "undefined method `#{symbol}' for #{self.mocha_inspect} which responds like #{@responder.mocha_inspect}"
164
- end
165
- if matching_expectation_allowing_invocation = @expectations.match_allowing_invocation(symbol, *arguments)
166
- matching_expectation_allowing_invocation.invoke(&block)
167
- else
168
- if (matching_expectation = @expectations.match(symbol, *arguments)) || (!matching_expectation && !@everything_stubbed)
169
- matching_expectation.invoke(&block) if matching_expectation
170
- message = UnexpectedInvocation.new(self, symbol, *arguments).to_s
171
- message << @mockery.mocha_inspect
172
- raise ExpectationError.new(message, caller)
173
- end
303
+ # @private
304
+ def all_expectations
305
+ @receiver.mocks.inject(ExpectationList.new) { |e, m| e + m.__expectations__ }
306
+ end
307
+
308
+ # @private
309
+ def method_missing(symbol, *arguments, &block) # rubocop:disable Style/MethodMissingSuper
310
+ check_expiry
311
+ check_responder_responds_to(symbol)
312
+ invocation = Invocation.new(self, symbol, *arguments, &block)
313
+ if (matching_expectation_allowing_invocation = all_expectations.match_allowing_invocation(invocation))
314
+ matching_expectation_allowing_invocation.invoke(invocation)
315
+ elsif (matching_expectation = all_expectations.match(invocation)) || (!matching_expectation && !@everything_stubbed)
316
+ raise_unexpected_invocation_error(invocation, matching_expectation)
174
317
  end
175
318
  end
176
319
 
177
- def respond_to?(symbol, include_private = false)
178
- if @responder then
320
+ # @private
321
+ def respond_to_missing?(symbol, include_private = false)
322
+ if @responder
179
323
  if @responder.method(:respond_to?).arity > 1
180
324
  @responder.respond_to?(symbol, include_private)
181
325
  else
182
326
  @responder.respond_to?(symbol)
183
327
  end
184
328
  else
185
- @everything_stubbed || @expectations.matches_method?(symbol)
329
+ @everything_stubbed || all_expectations.matches_method?(symbol)
186
330
  end
187
331
  end
188
332
 
333
+ if PRE_RUBY_V19
334
+ # @private
335
+ def respond_to?(symbol, include_private = false)
336
+ respond_to_missing?(symbol, include_private)
337
+ end
338
+ end
339
+
340
+ # @private
189
341
  def __verified__?(assertion_counter = nil)
190
342
  @expectations.verified?(assertion_counter)
191
343
  end
192
344
 
345
+ # @private
346
+ def __expire__
347
+ @expired = true
348
+ end
349
+
350
+ # @private
193
351
  def mocha_inspect
194
352
  @name.mocha_inspect
195
353
  end
196
354
 
355
+ # @private
197
356
  def inspect
198
357
  mocha_inspect
199
358
  end
200
359
 
360
+ # @private
201
361
  def ensure_method_not_already_defined(method_name)
202
- self.__metaclass__.send(:undef_method, method_name) if self.__metaclass__.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)
203
363
  end
204
364
 
365
+ # @private
205
366
  def any_expectations?
206
367
  @expectations.any?
207
368
  end
208
369
 
209
- # :startdoc:
370
+ private
210
371
 
211
- end
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
212
388
 
389
+ def check_expiry
390
+ if @expired # rubocop:disable Style/GuardClause
391
+ Deprecation.warning(
392
+ "#{mocha_inspect} was instantiated in one test but it is receiving invocations within another test.",
393
+ ' This can lead to unintended interactions between tests and hence unexpected test failures.',
394
+ ' Ensure that every test correctly cleans up any state that it introduces.',
395
+ ' A Mocha::StubbingError will be raised in this scenario in the future.'
396
+ )
397
+ end
398
+ end
399
+ end
213
400
  end