mocha 1.1.0 → 1.13.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 (263) hide show
  1. checksums.yaml +5 -5
  2. data/.github/FUNDING.yml +1 -0
  3. data/.rubocop.yml +61 -0
  4. data/.rubocop_todo.yml +27 -0
  5. data/.yardopts +1 -0
  6. data/CONTRIBUTING.md +4 -9
  7. data/README.md +114 -28
  8. data/RELEASE.md +248 -1
  9. data/Rakefile +53 -35
  10. data/gemfiles/Gemfile.test-unit.latest +5 -1
  11. data/init.rb +1 -3
  12. data/lib/mocha/any_instance_method.rb +12 -72
  13. data/lib/mocha/api.rb +121 -56
  14. data/lib/mocha/argument_iterator.rb +4 -8
  15. data/lib/mocha/backtrace_filter.rb +1 -5
  16. data/lib/mocha/block_matcher.rb +31 -0
  17. data/lib/mocha/cardinality.rb +60 -49
  18. data/lib/mocha/central.rb +21 -12
  19. data/lib/mocha/change_state_side_effect.rb +0 -4
  20. data/lib/mocha/class_methods.rb +19 -20
  21. data/lib/mocha/configuration.rb +361 -18
  22. data/lib/mocha/debug.rb +3 -2
  23. data/lib/mocha/deprecation.rb +8 -11
  24. data/lib/mocha/detection/mini_test.rb +0 -2
  25. data/lib/mocha/detection/test_unit.rb +3 -5
  26. data/lib/mocha/error_with_filtered_backtrace.rb +13 -0
  27. data/lib/mocha/exception_raiser.rb +4 -6
  28. data/lib/mocha/expectation.rb +125 -89
  29. data/lib/mocha/expectation_error.rb +1 -1
  30. data/lib/mocha/expectation_error_factory.rb +0 -1
  31. data/lib/mocha/expectation_list.rb +7 -11
  32. data/lib/mocha/hooks.rb +1 -3
  33. data/lib/mocha/in_state_ordering_constraint.rb +0 -4
  34. data/lib/mocha/inspect.rb +30 -38
  35. data/lib/mocha/instance_method.rb +15 -8
  36. data/lib/mocha/integration/mini_test/adapter.rb +2 -4
  37. data/lib/mocha/integration/mini_test/exception_translation.rb +1 -1
  38. data/lib/mocha/integration/mini_test/nothing.rb +4 -4
  39. data/lib/mocha/integration/mini_test/version_13.rb +4 -1
  40. data/lib/mocha/integration/mini_test/version_140.rb +4 -1
  41. data/lib/mocha/integration/mini_test/version_141.rb +4 -1
  42. data/lib/mocha/integration/mini_test/version_142_to_172.rb +4 -1
  43. data/lib/mocha/integration/mini_test/version_200.rb +4 -1
  44. data/lib/mocha/integration/mini_test/version_201_to_222.rb +4 -1
  45. data/lib/mocha/integration/mini_test/version_2110_to_2111.rb +4 -1
  46. data/lib/mocha/integration/mini_test/version_2112_to_320.rb +4 -1
  47. data/lib/mocha/integration/mini_test/version_230_to_2101.rb +4 -1
  48. data/lib/mocha/integration/mini_test.rb +7 -0
  49. data/lib/mocha/integration/monkey_patcher.rb +8 -2
  50. data/lib/mocha/integration/test_unit/adapter.rb +5 -6
  51. data/lib/mocha/integration/test_unit/gem_version_200.rb +5 -2
  52. data/lib/mocha/integration/test_unit/gem_version_201_to_202.rb +5 -2
  53. data/lib/mocha/integration/test_unit/gem_version_203_to_220.rb +5 -2
  54. data/lib/mocha/integration/test_unit/gem_version_230_to_250.rb +5 -2
  55. data/lib/mocha/integration/test_unit/nothing.rb +4 -4
  56. data/lib/mocha/integration/test_unit/ruby_version_185_and_below.rb +4 -1
  57. data/lib/mocha/integration/test_unit/ruby_version_186_and_above.rb +4 -1
  58. data/lib/mocha/integration/test_unit.rb +7 -0
  59. data/lib/mocha/integration.rb +2 -5
  60. data/lib/mocha/invocation.rb +77 -0
  61. data/lib/mocha/is_a.rb +0 -2
  62. data/lib/mocha/logger.rb +0 -4
  63. data/lib/mocha/macos_version.rb +5 -0
  64. data/lib/mocha/method_matcher.rb +1 -5
  65. data/lib/mocha/minitest.rb +8 -0
  66. data/lib/mocha/mock.rb +94 -46
  67. data/lib/mocha/mockery.rb +72 -98
  68. data/lib/mocha/names.rb +2 -12
  69. data/lib/mocha/not_initialized_error.rb +7 -0
  70. data/lib/mocha/object_methods.rb +25 -31
  71. data/lib/mocha/parameter_matchers/all_of.rb +2 -8
  72. data/lib/mocha/parameter_matchers/any_of.rb +2 -8
  73. data/lib/mocha/parameter_matchers/any_parameters.rb +3 -9
  74. data/lib/mocha/parameter_matchers/anything.rb +2 -8
  75. data/lib/mocha/parameter_matchers/base.rb +6 -12
  76. data/lib/mocha/parameter_matchers/equals.rb +1 -7
  77. data/lib/mocha/parameter_matchers/{query_string.rb → equivalent_uri.rb} +15 -15
  78. data/lib/mocha/parameter_matchers/has_entries.rb +2 -7
  79. data/lib/mocha/parameter_matchers/has_entry.rb +26 -21
  80. data/lib/mocha/parameter_matchers/has_key.rb +2 -7
  81. data/lib/mocha/parameter_matchers/has_keys.rb +53 -0
  82. data/lib/mocha/parameter_matchers/has_value.rb +2 -7
  83. data/lib/mocha/parameter_matchers/includes.rb +50 -8
  84. data/lib/mocha/parameter_matchers/instance_methods.rb +18 -0
  85. data/lib/mocha/parameter_matchers/instance_of.rb +0 -6
  86. data/lib/mocha/parameter_matchers/is_a.rb +2 -7
  87. data/lib/mocha/parameter_matchers/kind_of.rb +2 -6
  88. data/lib/mocha/parameter_matchers/not.rb +2 -7
  89. data/lib/mocha/parameter_matchers/optionally.rb +4 -10
  90. data/lib/mocha/parameter_matchers/regexp_matches.rb +0 -6
  91. data/lib/mocha/parameter_matchers/responds_with.rb +3 -8
  92. data/lib/mocha/parameter_matchers/yaml_equivalent.rb +2 -6
  93. data/lib/mocha/parameter_matchers.rb +3 -4
  94. data/lib/mocha/parameters_matcher.rb +6 -9
  95. data/lib/mocha/raised_exception.rb +11 -0
  96. data/lib/mocha/receivers.rb +10 -14
  97. data/lib/mocha/return_values.rb +4 -8
  98. data/lib/mocha/ruby_version.rb +4 -0
  99. data/lib/mocha/sequence.rb +4 -9
  100. data/lib/mocha/setup.rb +5 -0
  101. data/lib/mocha/single_return_value.rb +2 -5
  102. data/lib/mocha/singleton_class.rb +9 -0
  103. data/lib/mocha/state_machine.rb +33 -46
  104. data/lib/mocha/stubbed_method.rb +125 -0
  105. data/lib/mocha/stubbing_error.rb +2 -13
  106. data/lib/mocha/test_unit.rb +7 -2
  107. data/lib/mocha/thrower.rb +4 -6
  108. data/lib/mocha/thrown_object.rb +12 -0
  109. data/lib/mocha/version.rb +1 -1
  110. data/lib/mocha/yield_parameters.rb +7 -17
  111. data/lib/mocha.rb +8 -0
  112. data/mocha.gemspec +42 -40
  113. data/yard-templates/default/layout/html/google_analytics.erb +6 -9
  114. data/yard-templates/default/layout/html/setup.rb +2 -3
  115. metadata +45 -191
  116. data/bin/build-matrix +0 -71
  117. data/gemfiles/Gemfile.minitest.1.3.0 +0 -7
  118. data/gemfiles/Gemfile.minitest.1.4.0 +0 -7
  119. data/gemfiles/Gemfile.minitest.1.4.1 +0 -7
  120. data/gemfiles/Gemfile.minitest.1.4.2 +0 -7
  121. data/gemfiles/Gemfile.minitest.2.0.0 +0 -7
  122. data/gemfiles/Gemfile.minitest.2.0.1 +0 -7
  123. data/gemfiles/Gemfile.minitest.2.11.0 +0 -7
  124. data/gemfiles/Gemfile.minitest.2.11.2 +0 -7
  125. data/gemfiles/Gemfile.minitest.2.3.0 +0 -7
  126. data/gemfiles/Gemfile.test-unit.2.0.0 +0 -7
  127. data/gemfiles/Gemfile.test-unit.2.0.1 +0 -7
  128. data/gemfiles/Gemfile.test-unit.2.0.3 +0 -7
  129. data/lib/mocha/class_method.rb +0 -127
  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/single_yield.rb +0 -18
  138. data/lib/mocha/standalone.rb +0 -4
  139. data/lib/mocha/unexpected_invocation.rb +0 -26
  140. data/lib/mocha_standalone.rb +0 -4
  141. data/test/acceptance/acceptance_test_helper.rb +0 -41
  142. data/test/acceptance/bug_18914_test.rb +0 -43
  143. data/test/acceptance/bug_21465_test.rb +0 -34
  144. data/test/acceptance/bug_21563_test.rb +0 -25
  145. data/test/acceptance/exception_rescue_test.rb +0 -55
  146. data/test/acceptance/expectations_on_multiple_methods_test.rb +0 -55
  147. data/test/acceptance/expected_invocation_count_test.rb +0 -232
  148. data/test/acceptance/failure_messages_test.rb +0 -64
  149. data/test/acceptance/issue_65_test.rb +0 -63
  150. data/test/acceptance/issue_70_test.rb +0 -55
  151. data/test/acceptance/mocha_example_test.rb +0 -98
  152. data/test/acceptance/mocha_test_result_test.rb +0 -84
  153. data/test/acceptance/mock_test.rb +0 -100
  154. data/test/acceptance/mock_with_initializer_block_test.rb +0 -51
  155. data/test/acceptance/mocked_methods_dispatch_test.rb +0 -78
  156. data/test/acceptance/multiple_expectations_failure_message_test.rb +0 -68
  157. data/test/acceptance/optional_parameters_test.rb +0 -70
  158. data/test/acceptance/parameter_matcher_test.rb +0 -337
  159. data/test/acceptance/partial_mocks_test.rb +0 -47
  160. data/test/acceptance/prepend_test.rb +0 -88
  161. data/test/acceptance/raise_exception_test.rb +0 -39
  162. data/test/acceptance/return_value_test.rb +0 -52
  163. data/test/acceptance/sequence_test.rb +0 -192
  164. data/test/acceptance/states_test.rb +0 -70
  165. data/test/acceptance/stub_any_instance_method_defined_on_superclass_test.rb +0 -34
  166. data/test/acceptance/stub_any_instance_method_test.rb +0 -238
  167. data/test/acceptance/stub_class_method_defined_on_active_record_association_proxy_test.rb +0 -106
  168. data/test/acceptance/stub_class_method_defined_on_class_test.rb +0 -78
  169. data/test/acceptance/stub_class_method_defined_on_module_test.rb +0 -75
  170. data/test/acceptance/stub_class_method_defined_on_superclass_test.rb +0 -112
  171. data/test/acceptance/stub_everything_test.rb +0 -56
  172. data/test/acceptance/stub_instance_method_defined_on_active_record_association_proxy_test.rb +0 -93
  173. data/test/acceptance/stub_instance_method_defined_on_class_and_aliased_test.rb +0 -69
  174. data/test/acceptance/stub_instance_method_defined_on_class_test.rb +0 -69
  175. data/test/acceptance/stub_instance_method_defined_on_kernel_module_test.rb +0 -75
  176. data/test/acceptance/stub_instance_method_defined_on_module_test.rb +0 -75
  177. data/test/acceptance/stub_instance_method_defined_on_object_class_test.rb +0 -75
  178. data/test/acceptance/stub_instance_method_defined_on_singleton_class_test.rb +0 -70
  179. data/test/acceptance/stub_instance_method_defined_on_superclass_test.rb +0 -72
  180. data/test/acceptance/stub_module_method_test.rb +0 -163
  181. data/test/acceptance/stub_test.rb +0 -52
  182. data/test/acceptance/stubba_example_test.rb +0 -102
  183. data/test/acceptance/stubba_test_result_test.rb +0 -66
  184. data/test/acceptance/stubbing_error_backtrace_test.rb +0 -64
  185. data/test/acceptance/stubbing_frozen_object_test.rb +0 -88
  186. data/test/acceptance/stubbing_method_accepting_block_parameter_test.rb +0 -48
  187. data/test/acceptance/stubbing_method_unnecessarily_test.rb +0 -65
  188. data/test/acceptance/stubbing_nil_test.rb +0 -59
  189. data/test/acceptance/stubbing_non_existent_any_instance_method_test.rb +0 -130
  190. data/test/acceptance/stubbing_non_existent_class_method_test.rb +0 -157
  191. data/test/acceptance/stubbing_non_existent_instance_method_test.rb +0 -147
  192. data/test/acceptance/stubbing_non_public_any_instance_method_test.rb +0 -130
  193. data/test/acceptance/stubbing_non_public_class_method_test.rb +0 -163
  194. data/test/acceptance/stubbing_non_public_instance_method_test.rb +0 -143
  195. data/test/acceptance/stubbing_on_non_mock_object_test.rb +0 -64
  196. data/test/acceptance/stubbing_same_class_method_on_parent_and_child_classes_test.rb +0 -35
  197. data/test/acceptance/throw_test.rb +0 -45
  198. data/test/acceptance/unexpected_invocation_test.rb +0 -25
  199. data/test/acceptance/unstubbing_test.rb +0 -168
  200. data/test/assertions.rb +0 -6
  201. data/test/deprecation_disabler.rb +0 -15
  202. data/test/execution_point.rb +0 -36
  203. data/test/integration/mini_test_test.rb +0 -8
  204. data/test/integration/shared_tests.rb +0 -174
  205. data/test/integration/test_unit_test.rb +0 -8
  206. data/test/method_definer.rb +0 -24
  207. data/test/mini_test_result.rb +0 -90
  208. data/test/minitest_result.rb +0 -49
  209. data/test/simple_counter.rb +0 -13
  210. data/test/test_helper.rb +0 -50
  211. data/test/test_runner.rb +0 -58
  212. data/test/test_unit_result.rb +0 -20
  213. data/test/unit/any_instance_method_test.rb +0 -132
  214. data/test/unit/array_inspect_test.rb +0 -16
  215. data/test/unit/backtrace_filter_test.rb +0 -19
  216. data/test/unit/cardinality_test.rb +0 -56
  217. data/test/unit/central_test.rb +0 -100
  218. data/test/unit/change_state_side_effect_test.rb +0 -41
  219. data/test/unit/class_method_test.rb +0 -223
  220. data/test/unit/class_methods_test.rb +0 -40
  221. data/test/unit/configuration_test.rb +0 -38
  222. data/test/unit/date_time_inspect_test.rb +0 -21
  223. data/test/unit/exception_raiser_test.rb +0 -42
  224. data/test/unit/expectation_list_test.rb +0 -82
  225. data/test/unit/expectation_test.rb +0 -497
  226. data/test/unit/hash_inspect_test.rb +0 -16
  227. data/test/unit/hooks_test.rb +0 -29
  228. data/test/unit/in_state_ordering_constraint_test.rb +0 -43
  229. data/test/unit/method_matcher_test.rb +0 -28
  230. data/test/unit/mock_test.rb +0 -341
  231. data/test/unit/mockery_test.rb +0 -151
  232. data/test/unit/module_methods_test.rb +0 -19
  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_methods_test.rb +0 -46
  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 -129
  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 -59
  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 -32
  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/receivers_test.rb +0 -66
  256. data/test/unit/return_values_test.rb +0 -63
  257. data/test/unit/sequence_test.rb +0 -104
  258. data/test/unit/single_return_value_test.rb +0 -14
  259. data/test/unit/single_yield_test.rb +0 -18
  260. data/test/unit/state_machine_test.rb +0 -98
  261. data/test/unit/string_inspect_test.rb +0 -11
  262. data/test/unit/thrower_test.rb +0 -20
  263. data/test/unit/yield_parameters_test.rb +0 -93
@@ -0,0 +1,77 @@
1
+ require 'mocha/parameters_matcher'
2
+ require 'mocha/raised_exception'
3
+ require 'mocha/return_values'
4
+ require 'mocha/thrown_object'
5
+ require 'mocha/yield_parameters'
6
+ require 'mocha/configuration'
7
+ require 'mocha/deprecation'
8
+
9
+ module Mocha
10
+ class Invocation
11
+ attr_reader :method_name, :block
12
+
13
+ def initialize(mock, method_name, *arguments, &block)
14
+ @mock = mock
15
+ @method_name = method_name
16
+ @arguments = arguments
17
+ @block = block
18
+ @yields = []
19
+ @result = nil
20
+ end
21
+
22
+ def call(yield_parameters = YieldParameters.new, return_values = ReturnValues.new)
23
+ yield_parameters.next_invocation.each do |yield_args|
24
+ @yields << ParametersMatcher.new(yield_args)
25
+ if @block
26
+ @block.call(*yield_args)
27
+ else
28
+ raise LocalJumpError unless Mocha.configuration.reinstate_undocumented_behaviour_from_v1_9?
29
+ yield_args_description = ParametersMatcher.new(yield_args).mocha_inspect
30
+ Deprecation.warning(
31
+ "Stubbed method was instructed to yield #{yield_args_description}, but no block was given by invocation: #{call_description}.",
32
+ ' This will raise a LocalJumpError in the future.',
33
+ ' Use Expectation#with_block_given to constrain this expectation to match invocations supplying a block.',
34
+ ' And, if necessary, add another expectation to match invocations not supplying a block.'
35
+ )
36
+ end
37
+ end
38
+ return_values.next(self)
39
+ end
40
+
41
+ def returned(value)
42
+ @result = value
43
+ end
44
+
45
+ def raised(exception)
46
+ @result = RaisedException.new(exception)
47
+ end
48
+
49
+ def threw(tag, value)
50
+ @result = ThrownObject.new(tag, value)
51
+ end
52
+
53
+ def arguments
54
+ @arguments.dup
55
+ end
56
+
57
+ def call_description
58
+ description = "#{@mock.mocha_inspect}.#{@method_name}#{ParametersMatcher.new(@arguments).mocha_inspect}"
59
+ description << ' { ... }' unless @block.nil?
60
+ description
61
+ end
62
+
63
+ def short_call_description
64
+ "#{@method_name}(#{@arguments.join(', ')})"
65
+ end
66
+
67
+ def result_description
68
+ desc = "# => #{@result.mocha_inspect}"
69
+ desc << " after yielding #{@yields.map(&:mocha_inspect).join(', then ')}" if @yields.any?
70
+ desc
71
+ end
72
+
73
+ def full_description
74
+ "\n - #{call_description} #{result_description}"
75
+ end
76
+ end
77
+ end
data/lib/mocha/is_a.rb CHANGED
@@ -1,9 +1,7 @@
1
1
  class Object
2
-
3
2
  # :stopdoc:
4
3
 
5
4
  alias_method :__is_a__, :is_a?
6
5
 
7
6
  # :startdoc:
8
-
9
7
  end
data/lib/mocha/logger.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  module Mocha
2
-
3
2
  class Logger
4
-
5
3
  def initialize(io)
6
4
  @io = io
7
5
  end
@@ -9,7 +7,5 @@ module Mocha
9
7
  def warn(message)
10
8
  @io.puts "WARNING: #{message}"
11
9
  end
12
-
13
10
  end
14
-
15
11
  end
@@ -0,0 +1,5 @@
1
+ module Mocha
2
+ MACOS = /darwin/.match(RUBY_PLATFORM)
3
+ MACOS_VERSION = MACOS && /darwin(\d+)/.match(RUBY_PLATFORM)[1].to_i
4
+ MACOS_MOJAVE_VERSION = 18
5
+ end
@@ -1,7 +1,5 @@
1
1
  module Mocha
2
-
3
2
  class MethodMatcher
4
-
5
3
  attr_reader :expected_method_name
6
4
 
7
5
  def initialize(expected_method_name)
@@ -13,9 +11,7 @@ module Mocha
13
11
  end
14
12
 
15
13
  def mocha_inspect
16
- "#{@expected_method_name}"
14
+ @expected_method_name.to_s
17
15
  end
18
-
19
16
  end
20
-
21
17
  end
@@ -0,0 +1,8 @@
1
+ require 'mocha/integration/mini_test'
2
+ require 'mocha/deprecation'
3
+
4
+ unless Mocha::Integration::MiniTest.activate
5
+ Mocha::Deprecation.warning(
6
+ "MiniTest must be loaded *before* `require 'mocha/minitest'`."
7
+ )
8
+ end
data/lib/mocha/mock.rb CHANGED
@@ -1,20 +1,27 @@
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'
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/ruby_version'
11
12
 
12
13
  module Mocha
13
-
14
14
  # Traditional mock object.
15
15
  #
16
- # All methods return an {Expectation} which can be further modified by
17
- # 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.
18
25
  #
19
26
  # Stubs and expectations are basically the same thing. A stub is just an
20
27
  # expectation of zero or more invocations. The {#stubs} method is syntactic
@@ -67,15 +74,14 @@ module Mocha
67
74
  # different mock objects, use the {Expectation#in_sequence} method to
68
75
  # explicitly define a total or partial ordering of invocations.
69
76
  class Mock
70
-
71
77
  # Adds an expectation that the specified method must be called exactly once with any parameters.
72
78
  #
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.
79
+ # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
75
80
  #
76
81
  # @overload def expects(method_name)
82
+ # @param [Symbol,String] method_name name of expected method
77
83
  # @overload def expects(expected_methods_vs_return_values)
78
- # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
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.
79
85
  #
80
86
  # @example Expected method invoked once so no error raised
81
87
  # object = mock()
@@ -104,23 +110,23 @@ module Mocha
104
110
  # object.expects(:expected_method_two).returns(:result_two)
105
111
  def expects(method_name_or_hash, backtrace = 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
114
120
  end
115
121
 
116
122
  # Adds an expectation that the specified method may be called any number of times with any parameters.
117
123
  #
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.
124
+ # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
120
125
  #
121
126
  # @overload def stubs(method_name)
127
+ # @param [Symbol,String] method_name name of stubbed method
122
128
  # @overload def stubs(stubbed_methods_vs_return_values)
123
- # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
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.
124
130
  #
125
131
  # @example No error raised however many times stubbed method is invoked
126
132
  # object = mock()
@@ -140,19 +146,19 @@ module Mocha
140
146
  # object.stubs(:stubbed_method_two).returns(:result_two)
141
147
  def stubs(method_name_or_hash, backtrace = nil)
142
148
  iterator = ArgumentIterator.new(method_name_or_hash)
143
- iterator.each { |*args|
149
+ iterator.each do |*args|
144
150
  method_name = args.shift
145
151
  ensure_method_not_already_defined(method_name)
146
152
  expectation = Expectation.new(self, method_name, backtrace)
147
153
  expectation.at_least(0)
148
- expectation.returns(args.shift) if args.length > 0
154
+ expectation.returns(args.shift) unless args.empty?
149
155
  @expectations.add(expectation)
150
- }
156
+ end
151
157
  end
152
158
 
153
- # Removes the specified stubbed method (added by calls to {#expects} or {#stubs}) and all expectations associated with it.
159
+ # Removes the specified stubbed methods (added by calls to {#expects} or {#stubs}) and all expectations associated with them.
154
160
  #
155
- # @param [Symbol] method_name name of method to unstub.
161
+ # @param [Array<Symbol>] method_names names of methods to unstub.
156
162
  #
157
163
  # @example Invoking an unstubbed method causes error to be raised
158
164
  # object = mock('mock') do
@@ -160,8 +166,18 @@ module Mocha
160
166
  # object.stubbed_method # => :result_one
161
167
  # object.unstub(:stubbed_method)
162
168
  # object.stubbed_method # => unexpected invocation: #<Mock:mock>.stubbed_method()
163
- def unstub(method_name)
164
- @expectations.remove_all_matching_method(method_name)
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
165
181
  end
166
182
 
167
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.
@@ -251,7 +267,7 @@ module Mocha
251
267
  end
252
268
 
253
269
  # @private
254
- def initialize(mockery, name = nil, receiver = nil, &block)
270
+ def initialize(mockery, name = nil, receiver = nil)
255
271
  @mockery = mockery
256
272
  @name = name || DefaultName.new(self)
257
273
  @receiver = receiver || DefaultReceiver.new(self)
@@ -259,7 +275,7 @@ module Mocha
259
275
  @everything_stubbed = false
260
276
  @responder = nil
261
277
  @unexpected_invocation = nil
262
- instance_eval(&block) if block
278
+ @expired = false
263
279
  end
264
280
 
265
281
  # @private
@@ -269,6 +285,8 @@ module Mocha
269
285
 
270
286
  alias_method :__stubs__, :stubs
271
287
 
288
+ alias_method :__singleton_class__, :singleton_class
289
+
272
290
  alias_method :quacks_like, :responds_like
273
291
  alias_method :quacks_like_instance_of, :responds_like_instance_of
274
292
 
@@ -288,30 +306,20 @@ module Mocha
288
306
  end
289
307
 
290
308
  # @private
291
- 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
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)
309
317
  end
310
318
  end
311
319
 
312
320
  # @private
313
- def respond_to?(symbol, include_private = false)
314
- if @responder then
321
+ def respond_to_missing?(symbol, include_private = false)
322
+ if @responder
315
323
  if @responder.method(:respond_to?).arity > 1
316
324
  @responder.respond_to?(symbol, include_private)
317
325
  else
@@ -322,11 +330,23 @@ module Mocha
322
330
  end
323
331
  end
324
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
+
325
340
  # @private
326
341
  def __verified__?(assertion_counter = nil)
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
351
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
+ 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
352
400
  end
data/lib/mocha/mockery.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'mocha/ruby_version'
1
2
  require 'mocha/central'
2
3
  require 'mocha/mock'
3
4
  require 'mocha/names'
@@ -6,16 +7,42 @@ require 'mocha/state_machine'
6
7
  require 'mocha/logger'
7
8
  require 'mocha/configuration'
8
9
  require 'mocha/stubbing_error'
10
+ require 'mocha/not_initialized_error'
9
11
  require 'mocha/expectation_error_factory'
10
12
 
11
13
  module Mocha
12
-
13
14
  class Mockery
15
+ class Null < self
16
+ def add_mock(*)
17
+ raise_not_initialized_error
18
+ end
14
19
 
15
- class << self
20
+ def add_state_machine(*)
21
+ raise_not_initialized_error
22
+ end
23
+
24
+ def stubba
25
+ Central::Null.new(&method(:raise_not_initialized_error))
26
+ end
16
27
 
28
+ private
29
+
30
+ def raise_not_initialized_error
31
+ message = 'Mocha methods cannot be used outside the context of a test'
32
+ raise NotInitializedError.new(message, caller)
33
+ end
34
+ end
35
+
36
+ class << self
17
37
  def instance
18
- @instance ||= new
38
+ @instances.last || Null.new
39
+ end
40
+
41
+ def setup
42
+ @instances ||= []
43
+ mockery = new
44
+ mockery.logger = instance.logger unless @instances.empty?
45
+ @instances.push(mockery)
19
46
  end
20
47
 
21
48
  def verify(*args)
@@ -24,28 +51,25 @@ module Mocha
24
51
 
25
52
  def teardown
26
53
  instance.teardown
54
+ ensure
55
+ @instances.pop
27
56
  end
28
-
29
- def reset_instance
30
- @instance = nil
31
- end
32
-
33
57
  end
34
58
 
35
- def named_mock(name, &block)
36
- add_mock(Mock.new(self, Name.new(name), &block))
59
+ def named_mock(name)
60
+ add_mock(Mock.new(self, Name.new(name)))
37
61
  end
38
62
 
39
- def unnamed_mock(&block)
40
- add_mock(Mock.new(self, &block))
63
+ def unnamed_mock
64
+ add_mock(Mock.new(self))
41
65
  end
42
66
 
43
- def mock_impersonating(object, &block)
44
- add_mock(Mock.new(self, ImpersonatingName.new(object), ObjectReceiver.new(object), &block))
67
+ def mock_impersonating(object)
68
+ add_mock(Mock.new(self, ImpersonatingName.new(object), ObjectReceiver.new(object)))
45
69
  end
46
70
 
47
- def mock_impersonating_any_instance_of(klass, &block)
48
- add_mock(Mock.new(self, ImpersonatingAnyInstanceName.new(klass), AnyInstanceReceiver.new(klass), &block))
71
+ def mock_impersonating_any_instance_of(klass)
72
+ add_mock(Mock.new(self, ImpersonatingAnyInstanceName.new(klass), AnyInstanceReceiver.new(klass)))
49
73
  end
50
74
 
51
75
  def new_state_machine(name)
@@ -55,24 +79,22 @@ module Mocha
55
79
  def verify(assertion_counter = nil)
56
80
  unless mocks.all? { |mock| mock.__verified__?(assertion_counter) }
57
81
  message = "not all expectations were satisfied\n#{mocha_inspect}"
58
- if unsatisfied_expectations.empty?
59
- backtrace = caller
60
- else
61
- backtrace = unsatisfied_expectations[0].backtrace
62
- end
82
+ backtrace = if unsatisfied_expectations.empty?
83
+ caller
84
+ else
85
+ unsatisfied_expectations[0].backtrace
86
+ end
63
87
  raise ExpectationErrorFactory.build(message, backtrace)
64
88
  end
65
- expectations.each do |e|
66
- unless Mocha::Configuration.allow?(:stubbing_method_unnecessarily)
67
- unless e.used?
68
- on_stubbing_method_unnecessarily(e)
69
- end
70
- end
89
+ expectations.reject(&:used?).each do |expectation|
90
+ signature_proc = lambda { expectation.method_signature }
91
+ check(:stubbing_method_unnecessarily, 'method unnecessarily', signature_proc, expectation.backtrace)
71
92
  end
72
93
  end
73
94
 
74
95
  def teardown
75
96
  stubba.unstub_all
97
+ mocks.each(&:__expire__)
76
98
  reset
77
99
  end
78
100
 
@@ -89,78 +111,24 @@ module Mocha
89
111
  end
90
112
 
91
113
  def mocha_inspect
92
- message = ""
93
- message << "unsatisfied expectations:\n- #{unsatisfied_expectations.map { |e| e.mocha_inspect }.join("\n- ")}\n" unless unsatisfied_expectations.empty?
94
- message << "satisfied expectations:\n- #{satisfied_expectations.map { |e| e.mocha_inspect }.join("\n- ")}\n" unless satisfied_expectations.empty?
95
- message << "states:\n- #{state_machines.map { |sm| sm.mocha_inspect }.join("\n- ")}" unless state_machines.empty?
114
+ message = ''
115
+ message << "unsatisfied expectations:\n- #{unsatisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if unsatisfied_expectations.any?
116
+ message << "satisfied expectations:\n- #{satisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if satisfied_expectations.any?
117
+ message << "states:\n- #{state_machines.map(&:mocha_inspect).join("\n- ")}\n" if state_machines.any?
96
118
  message
97
119
  end
98
120
 
99
121
  def on_stubbing(object, method)
100
- method = RUBY_VERSION < '1.9' ? method.to_s : method.to_sym
101
- unless Mocha::Configuration.allow?(:stubbing_non_existent_method)
102
- unless object.method_exists?(method, include_public_methods = true)
103
- on_stubbing_non_existent_method(object, method)
104
- end
105
- end
106
- unless Mocha::Configuration.allow?(:stubbing_non_public_method)
107
- if object.method_exists?(method, include_public_methods = false)
108
- on_stubbing_non_public_method(object, method)
109
- end
110
- end
111
- unless Mocha::Configuration.allow?(:stubbing_method_on_nil)
112
- if object.nil?
113
- on_stubbing_method_on_nil(object, method)
114
- end
115
- end
116
- unless Mocha::Configuration.allow?(:stubbing_method_on_non_mock_object)
117
- on_stubbing_method_on_non_mock_object(object, method)
118
- end
119
- end
120
-
121
- def on_stubbing_non_existent_method(object, method)
122
- if Mocha::Configuration.prevent?(:stubbing_non_existent_method)
123
- raise StubbingError.new("stubbing non-existent method: #{object.mocha_inspect}.#{method}", caller)
122
+ method = PRE_RUBY_V19 ? method.to_s : method.to_sym
123
+ signature_proc = lambda { "#{object.mocha_inspect}.#{method}" }
124
+ check(:stubbing_non_existent_method, 'non-existent method', signature_proc) do
125
+ !(object.stubba_class.__method_exists__?(method, true) || object.respond_to?(method.to_sym))
124
126
  end
125
- if Mocha::Configuration.warn_when?(:stubbing_non_existent_method)
126
- logger.warn "stubbing non-existent method: #{object.mocha_inspect}.#{method}"
127
- end
128
- end
129
-
130
- def on_stubbing_non_public_method(object, method)
131
- if Mocha::Configuration.prevent?(:stubbing_non_public_method)
132
- raise StubbingError.new("stubbing non-public method: #{object.mocha_inspect}.#{method}", caller)
133
- end
134
- if Mocha::Configuration.warn_when?(:stubbing_non_public_method)
135
- logger.warn "stubbing non-public method: #{object.mocha_inspect}.#{method}"
136
- end
137
- end
138
-
139
- def on_stubbing_method_on_nil(object, method)
140
- if Mocha::Configuration.prevent?(:stubbing_method_on_nil)
141
- raise StubbingError.new("stubbing method on nil: #{object.mocha_inspect}.#{method}", caller)
142
- end
143
- if Mocha::Configuration.warn_when?(:stubbing_method_on_nil)
144
- logger.warn "stubbing method on nil: #{object.mocha_inspect}.#{method}"
145
- end
146
- end
147
-
148
- def on_stubbing_method_on_non_mock_object(object, method)
149
- if Mocha::Configuration.prevent?(:stubbing_method_on_non_mock_object)
150
- raise StubbingError.new("stubbing method on non-mock object: #{object.mocha_inspect}.#{method}", caller)
151
- end
152
- if Mocha::Configuration.warn_when?(:stubbing_method_on_non_mock_object)
153
- logger.warn "stubbing method on non-mock object: #{object.mocha_inspect}.#{method}"
154
- end
155
- end
156
-
157
- def on_stubbing_method_unnecessarily(expectation)
158
- if Mocha::Configuration.prevent?(:stubbing_method_unnecessarily)
159
- raise StubbingError.new("stubbing method unnecessarily: #{expectation.method_signature}", expectation.backtrace)
160
- end
161
- if Mocha::Configuration.warn_when?(:stubbing_method_unnecessarily)
162
- logger.warn "stubbing method unnecessarily: #{expectation.method_signature}"
127
+ check(:stubbing_non_public_method, 'non-public method', signature_proc) do
128
+ object.stubba_class.__method_exists__?(method, false)
163
129
  end
130
+ check(:stubbing_method_on_nil, 'method on nil', signature_proc) { object.nil? }
131
+ check(:stubbing_method_on_non_mock_object, 'method on non-mock object', signature_proc)
164
132
  end
165
133
 
166
134
  attr_writer :logger
@@ -169,19 +137,27 @@ module Mocha
169
137
  @logger ||= Logger.new($stderr)
170
138
  end
171
139
 
172
-
173
140
  private
174
141
 
142
+ def check(action, description, signature_proc, backtrace = caller)
143
+ treatment = Mocha.configuration.send(action)
144
+ return if (treatment == :allow) || (block_given? && !yield)
145
+ method_signature = signature_proc.call
146
+ message = "stubbing #{description}: #{method_signature}"
147
+ raise StubbingError.new(message, backtrace) if treatment == :prevent
148
+ logger.warn(message) if treatment == :warn
149
+ end
150
+
175
151
  def expectations
176
152
  mocks.map { |mock| mock.__expectations__.to_a }.flatten
177
153
  end
178
154
 
179
155
  def unsatisfied_expectations
180
- expectations.reject { |e| e.verified? }
156
+ expectations.reject(&:verified?)
181
157
  end
182
158
 
183
159
  def satisfied_expectations
184
- expectations.select { |e| e.verified? }
160
+ expectations.select(&:verified?)
185
161
  end
186
162
 
187
163
  def add_mock(mock)
@@ -199,7 +175,5 @@ module Mocha
199
175
  @mocks = nil
200
176
  @state_machines = nil
201
177
  end
202
-
203
178
  end
204
-
205
179
  end