mocha 1.10.2 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.yardopts +1 -0
  4. data/CONTRIBUTING.md +1 -1
  5. data/README.md +28 -30
  6. data/RELEASE.md +87 -0
  7. data/Rakefile +7 -5
  8. data/lib/mocha/api.rb +2 -3
  9. data/lib/mocha/block_matcher.rb +31 -0
  10. data/lib/mocha/cardinality.rb +30 -27
  11. data/lib/mocha/configuration.rb +2 -2
  12. data/lib/mocha/expectation.rb +71 -30
  13. data/lib/mocha/inspect.rb +6 -4
  14. data/lib/mocha/invocation.rb +9 -5
  15. data/lib/mocha/mock.rb +41 -15
  16. data/lib/mocha/mockery.rb +17 -27
  17. data/lib/mocha/parameter_matchers.rb +1 -0
  18. data/lib/mocha/parameter_matchers/has_entries.rb +2 -3
  19. data/lib/mocha/parameter_matchers/has_entry.rb +24 -16
  20. data/lib/mocha/parameter_matchers/has_key.rb +2 -3
  21. data/lib/mocha/parameter_matchers/has_keys.rb +53 -0
  22. data/lib/mocha/parameter_matchers/has_value.rb +2 -3
  23. data/lib/mocha/parameter_matchers/is_a.rb +2 -3
  24. data/lib/mocha/parameter_matchers/not.rb +2 -3
  25. data/lib/mocha/state_machine.rb +31 -40
  26. data/lib/mocha/stubbed_method.rb +4 -6
  27. data/lib/mocha/version.rb +1 -1
  28. data/lib/mocha/yield_parameters.rb +5 -11
  29. data/mocha.gemspec +13 -7
  30. metadata +8 -210
  31. data/bin/build-matrix +0 -82
  32. data/docs/CNAME +0 -1
  33. data/docs/Mocha.html +0 -254
  34. data/docs/Mocha/API.html +0 -1287
  35. data/docs/Mocha/ClassMethods.html +0 -264
  36. data/docs/Mocha/Configuration.html +0 -1525
  37. data/docs/Mocha/Expectation.html +0 -2654
  38. data/docs/Mocha/ExpectationError.html +0 -152
  39. data/docs/Mocha/ExpectationErrorFactory.html +0 -260
  40. data/docs/Mocha/Hooks.html +0 -370
  41. data/docs/Mocha/Integration.html +0 -125
  42. data/docs/Mocha/Integration/MiniTest.html +0 -123
  43. data/docs/Mocha/Integration/MiniTest/Adapter.html +0 -164
  44. data/docs/Mocha/Integration/TestUnit.html +0 -123
  45. data/docs/Mocha/Integration/TestUnit/Adapter.html +0 -164
  46. data/docs/Mocha/Mock.html +0 -1237
  47. data/docs/Mocha/ObjectMethods.html +0 -765
  48. data/docs/Mocha/ParameterMatchers.html +0 -2961
  49. data/docs/Mocha/ParameterMatchers/AllOf.html +0 -153
  50. data/docs/Mocha/ParameterMatchers/AnyOf.html +0 -153
  51. data/docs/Mocha/ParameterMatchers/AnyParameters.html +0 -153
  52. data/docs/Mocha/ParameterMatchers/Anything.html +0 -153
  53. data/docs/Mocha/ParameterMatchers/Base.html +0 -441
  54. data/docs/Mocha/ParameterMatchers/Equals.html +0 -153
  55. data/docs/Mocha/ParameterMatchers/EquivalentUri.html +0 -153
  56. data/docs/Mocha/ParameterMatchers/HasEntries.html +0 -153
  57. data/docs/Mocha/ParameterMatchers/HasEntry.html +0 -153
  58. data/docs/Mocha/ParameterMatchers/HasKey.html +0 -153
  59. data/docs/Mocha/ParameterMatchers/HasValue.html +0 -153
  60. data/docs/Mocha/ParameterMatchers/Includes.html +0 -153
  61. data/docs/Mocha/ParameterMatchers/InstanceOf.html +0 -153
  62. data/docs/Mocha/ParameterMatchers/IsA.html +0 -153
  63. data/docs/Mocha/ParameterMatchers/KindOf.html +0 -153
  64. data/docs/Mocha/ParameterMatchers/Not.html +0 -153
  65. data/docs/Mocha/ParameterMatchers/Optionally.html +0 -153
  66. data/docs/Mocha/ParameterMatchers/RegexpMatches.html +0 -153
  67. data/docs/Mocha/ParameterMatchers/RespondsWith.html +0 -153
  68. data/docs/Mocha/ParameterMatchers/YamlEquivalent.html +0 -153
  69. data/docs/Mocha/Sequence.html +0 -149
  70. data/docs/Mocha/StateMachine.html +0 -527
  71. data/docs/Mocha/StateMachine/State.html +0 -140
  72. data/docs/Mocha/StateMachine/StatePredicate.html +0 -140
  73. data/docs/Mocha/StubbingError.html +0 -150
  74. data/docs/_index.html +0 -519
  75. data/docs/class_list.html +0 -51
  76. data/docs/css/common.css +0 -1
  77. data/docs/css/full_list.css +0 -58
  78. data/docs/css/style.css +0 -496
  79. data/docs/file.COPYING.html +0 -81
  80. data/docs/file.MIT-LICENSE.html +0 -85
  81. data/docs/file.README.html +0 -448
  82. data/docs/file.RELEASE.html +0 -974
  83. data/docs/file_list.html +0 -71
  84. data/docs/frames.html +0 -17
  85. data/docs/index.html +0 -448
  86. data/docs/js/app.js +0 -303
  87. data/docs/js/full_list.js +0 -216
  88. data/docs/js/jquery.js +0 -4
  89. data/docs/method_list.html +0 -635
  90. data/docs/top-level-namespace.html +0 -118
  91. data/gemfiles/Gemfile.minitest.1.3.0 +0 -7
  92. data/gemfiles/Gemfile.minitest.1.4.0 +0 -7
  93. data/gemfiles/Gemfile.minitest.1.4.1 +0 -7
  94. data/gemfiles/Gemfile.minitest.1.4.2 +0 -7
  95. data/gemfiles/Gemfile.minitest.2.0.0 +0 -7
  96. data/gemfiles/Gemfile.minitest.2.0.1 +0 -7
  97. data/gemfiles/Gemfile.minitest.2.11.0 +0 -7
  98. data/gemfiles/Gemfile.minitest.2.11.2 +0 -7
  99. data/gemfiles/Gemfile.minitest.2.3.0 +0 -7
  100. data/gemfiles/Gemfile.minitest.5.11.3 +0 -7
  101. data/gemfiles/Gemfile.test-unit.2.0.0 +0 -7
  102. data/gemfiles/Gemfile.test-unit.2.0.1 +0 -7
  103. data/gemfiles/Gemfile.test-unit.2.0.3 +0 -7
  104. data/lib/mocha/multiple_yields.rb +0 -15
  105. data/lib/mocha/no_yields.rb +0 -5
  106. data/lib/mocha/pretty_parameters.rb +0 -24
  107. data/lib/mocha/single_yield.rb +0 -13
  108. data/test/acceptance/acceptance_test_helper.rb +0 -41
  109. data/test/acceptance/bug_18914_test.rb +0 -37
  110. data/test/acceptance/bug_21465_test.rb +0 -31
  111. data/test/acceptance/bug_21563_test.rb +0 -22
  112. data/test/acceptance/display_matching_invocations_alongside_expectations_test.rb +0 -69
  113. data/test/acceptance/exception_rescue_test.rb +0 -53
  114. data/test/acceptance/expectations_on_multiple_methods_test.rb +0 -55
  115. data/test/acceptance/expected_invocation_count_test.rb +0 -229
  116. data/test/acceptance/failure_messages_test.rb +0 -61
  117. data/test/acceptance/issue_272_test.rb +0 -50
  118. data/test/acceptance/issue_65_test.rb +0 -64
  119. data/test/acceptance/issue_70_test.rb +0 -53
  120. data/test/acceptance/mocha_example_test.rb +0 -96
  121. data/test/acceptance/mocha_test_result_test.rb +0 -83
  122. data/test/acceptance/mock_built_with_first_argument_type_being_string_test.rb +0 -99
  123. data/test/acceptance/mock_test.rb +0 -139
  124. data/test/acceptance/mocked_methods_dispatch_test.rb +0 -75
  125. data/test/acceptance/multiple_expectations_failure_message_test.rb +0 -66
  126. data/test/acceptance/optional_parameters_test.rb +0 -67
  127. data/test/acceptance/parameter_matcher_test.rb +0 -299
  128. data/test/acceptance/partial_mocks_test.rb +0 -44
  129. data/test/acceptance/prepend_test.rb +0 -86
  130. data/test/acceptance/prevent_use_of_mocha_outside_test_test.rb +0 -76
  131. data/test/acceptance/raise_exception_test.rb +0 -36
  132. data/test/acceptance/return_value_test.rb +0 -49
  133. data/test/acceptance/sequence_test.rb +0 -189
  134. data/test/acceptance/states_test.rb +0 -71
  135. data/test/acceptance/stub_any_instance_method_defined_on_superclass_test.rb +0 -64
  136. data/test/acceptance/stub_any_instance_method_test.rb +0 -301
  137. data/test/acceptance/stub_class_method_defined_on_active_record_association_proxy_test.rb +0 -105
  138. data/test/acceptance/stub_class_method_defined_on_class_test.rb +0 -78
  139. data/test/acceptance/stub_class_method_defined_on_module_test.rb +0 -73
  140. data/test/acceptance/stub_class_method_defined_on_superclass_test.rb +0 -144
  141. data/test/acceptance/stub_everything_test.rb +0 -53
  142. data/test/acceptance/stub_instance_method_defined_on_active_record_association_proxy_test.rb +0 -91
  143. data/test/acceptance/stub_instance_method_defined_on_class_and_aliased_test.rb +0 -67
  144. data/test/acceptance/stub_instance_method_defined_on_class_test.rb +0 -67
  145. data/test/acceptance/stub_instance_method_defined_on_kernel_module_test.rb +0 -136
  146. data/test/acceptance/stub_instance_method_defined_on_module_test.rb +0 -76
  147. data/test/acceptance/stub_instance_method_defined_on_object_class_test.rb +0 -75
  148. data/test/acceptance/stub_instance_method_defined_on_singleton_class_test.rb +0 -67
  149. data/test/acceptance/stub_instance_method_defined_on_superclass_test.rb +0 -70
  150. data/test/acceptance/stub_method_defined_on_module_and_aliased_test.rb +0 -38
  151. data/test/acceptance/stub_module_method_test.rb +0 -200
  152. data/test/acceptance/stub_test.rb +0 -49
  153. data/test/acceptance/stubba_example_test.rb +0 -93
  154. data/test/acceptance/stubba_test_result_test.rb +0 -71
  155. data/test/acceptance/stubbing_error_backtrace_test.rb +0 -63
  156. data/test/acceptance/stubbing_frozen_object_test.rb +0 -88
  157. data/test/acceptance/stubbing_method_accepting_block_parameter_test.rb +0 -52
  158. data/test/acceptance/stubbing_method_unnecessarily_test.rb +0 -63
  159. data/test/acceptance/stubbing_nil_test.rb +0 -60
  160. data/test/acceptance/stubbing_non_existent_any_instance_method_test.rb +0 -157
  161. data/test/acceptance/stubbing_non_existent_class_method_test.rb +0 -157
  162. data/test/acceptance/stubbing_non_existent_instance_method_test.rb +0 -145
  163. data/test/acceptance/stubbing_non_public_any_instance_method_test.rb +0 -128
  164. data/test/acceptance/stubbing_non_public_class_method_test.rb +0 -163
  165. data/test/acceptance/stubbing_non_public_instance_method_test.rb +0 -141
  166. data/test/acceptance/stubbing_on_non_mock_object_test.rb +0 -70
  167. data/test/acceptance/stubbing_same_class_method_on_parent_and_child_classes_test.rb +0 -33
  168. data/test/acceptance/throw_test.rb +0 -42
  169. data/test/acceptance/unexpected_invocation_test.rb +0 -23
  170. data/test/acceptance/unstubbing_test.rb +0 -194
  171. data/test/assertions.rb +0 -8
  172. data/test/deprecation_disabler.rb +0 -15
  173. data/test/execution_point.rb +0 -36
  174. data/test/integration/mini_test_test.rb +0 -8
  175. data/test/integration/shared_tests.rb +0 -178
  176. data/test/integration/test_unit_test.rb +0 -8
  177. data/test/method_definer.rb +0 -16
  178. data/test/mini_test_result.rb +0 -96
  179. data/test/minitest_result.rb +0 -48
  180. data/test/simple_counter.rb +0 -11
  181. data/test/test_helper.rb +0 -58
  182. data/test/test_runner.rb +0 -58
  183. data/test/test_unit_result.rb +0 -22
  184. data/test/unit/any_instance_method_test.rb +0 -175
  185. data/test/unit/array_inspect_test.rb +0 -14
  186. data/test/unit/backtrace_filter_test.rb +0 -17
  187. data/test/unit/cardinality_test.rb +0 -72
  188. data/test/unit/central_test.rb +0 -98
  189. data/test/unit/change_state_side_effect_test.rb +0 -37
  190. data/test/unit/class_methods_test.rb +0 -69
  191. data/test/unit/configuration_test.rb +0 -37
  192. data/test/unit/date_time_inspect_test.rb +0 -19
  193. data/test/unit/exception_raiser_test.rb +0 -45
  194. data/test/unit/expectation_list_test.rb +0 -82
  195. data/test/unit/expectation_test.rb +0 -492
  196. data/test/unit/hash_inspect_test.rb +0 -14
  197. data/test/unit/hooks_test.rb +0 -35
  198. data/test/unit/in_state_ordering_constraint_test.rb +0 -39
  199. data/test/unit/instance_method_test.rb +0 -282
  200. data/test/unit/method_matcher_test.rb +0 -26
  201. data/test/unit/mock_test.rb +0 -372
  202. data/test/unit/mockery_test.rb +0 -171
  203. data/test/unit/module_methods_test.rb +0 -16
  204. data/test/unit/multiple_yields_test.rb +0 -16
  205. data/test/unit/no_yields_test.rb +0 -16
  206. data/test/unit/object_inspect_test.rb +0 -60
  207. data/test/unit/object_methods_test.rb +0 -63
  208. data/test/unit/parameter_matchers/all_of_test.rb +0 -24
  209. data/test/unit/parameter_matchers/any_of_test.rb +0 -24
  210. data/test/unit/parameter_matchers/anything_test.rb +0 -19
  211. data/test/unit/parameter_matchers/equals_test.rb +0 -23
  212. data/test/unit/parameter_matchers/equivalent_uri_test.rb +0 -41
  213. data/test/unit/parameter_matchers/has_entries_test.rb +0 -51
  214. data/test/unit/parameter_matchers/has_entry_test.rb +0 -128
  215. data/test/unit/parameter_matchers/has_key_test.rb +0 -54
  216. data/test/unit/parameter_matchers/has_value_test.rb +0 -55
  217. data/test/unit/parameter_matchers/includes_test.rb +0 -106
  218. data/test/unit/parameter_matchers/instance_of_test.rb +0 -23
  219. data/test/unit/parameter_matchers/is_a_test.rb +0 -23
  220. data/test/unit/parameter_matchers/kind_of_test.rb +0 -23
  221. data/test/unit/parameter_matchers/not_test.rb +0 -24
  222. data/test/unit/parameter_matchers/regexp_matches_test.rb +0 -45
  223. data/test/unit/parameter_matchers/responds_with_test.rb +0 -38
  224. data/test/unit/parameter_matchers/stub_matcher.rb +0 -23
  225. data/test/unit/parameter_matchers/yaml_equivalent_test.rb +0 -23
  226. data/test/unit/parameters_matcher_test.rb +0 -119
  227. data/test/unit/receivers_test.rb +0 -96
  228. data/test/unit/return_values_test.rb +0 -66
  229. data/test/unit/sequence_test.rb +0 -100
  230. data/test/unit/single_return_value_test.rb +0 -17
  231. data/test/unit/single_yield_test.rb +0 -16
  232. data/test/unit/state_machine_test.rb +0 -96
  233. data/test/unit/string_inspect_test.rb +0 -9
  234. data/test/unit/thrower_test.rb +0 -23
  235. data/test/unit/yield_parameters_test.rb +0 -91
data/lib/mocha/inspect.rb CHANGED
@@ -11,14 +11,16 @@ module Mocha
11
11
  end
12
12
 
13
13
  module ArrayMethods
14
- def mocha_inspect
15
- "[#{collect(&:mocha_inspect).join(', ')}]"
14
+ def mocha_inspect(wrapped = true)
15
+ unwrapped = collect(&:mocha_inspect).join(', ')
16
+ wrapped ? "[#{unwrapped}]" : unwrapped
16
17
  end
17
18
  end
18
19
 
19
20
  module HashMethods
20
- def mocha_inspect
21
- "{#{collect { |key, value| "#{key.mocha_inspect} => #{value.mocha_inspect}" }.join(', ')}}"
21
+ def mocha_inspect(wrapped = true)
22
+ unwrapped = collect { |key, value| "#{key.mocha_inspect} => #{value.mocha_inspect}" }.join(', ')
23
+ wrapped ? "{#{unwrapped}}" : unwrapped
22
24
  end
23
25
  end
24
26
 
@@ -8,7 +8,7 @@ require 'mocha/deprecation'
8
8
 
9
9
  module Mocha
10
10
  class Invocation
11
- attr_reader :method_name
11
+ attr_reader :method_name, :block
12
12
 
13
13
  def initialize(mock, method_name, *arguments, &block)
14
14
  @mock = mock
@@ -27,10 +27,12 @@ module Mocha
27
27
  else
28
28
  raise LocalJumpError unless Mocha.configuration.reinstate_undocumented_behaviour_from_v1_9?
29
29
  yield_args_description = ParametersMatcher.new(yield_args).mocha_inspect
30
- Deprecation.warning([
30
+ Deprecation.warning(
31
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
- ].join(' '))
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
+ )
34
36
  end
35
37
  end
36
38
  return_values.next(self)
@@ -53,7 +55,9 @@ module Mocha
53
55
  end
54
56
 
55
57
  def call_description
56
- "#{@mock.mocha_inspect}.#{@method_name}#{ParametersMatcher.new(@arguments).mocha_inspect}"
58
+ description = "#{@mock.mocha_inspect}.#{@method_name}#{ParametersMatcher.new(@arguments).mocha_inspect}"
59
+ description << ' { ... }' unless @block.nil?
60
+ description
57
61
  end
58
62
 
59
63
  def short_call_description
data/lib/mocha/mock.rb CHANGED
@@ -275,6 +275,7 @@ module Mocha
275
275
  @everything_stubbed = false
276
276
  @responder = nil
277
277
  @unexpected_invocation = nil
278
+ @expired = false
278
279
  end
279
280
 
280
281
  # @private
@@ -305,26 +306,16 @@ module Mocha
305
306
  end
306
307
 
307
308
  # @private
308
- # rubocop:disable Style/MethodMissingSuper,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
309
- def method_missing(symbol, *arguments, &block)
310
- if @responder && !@responder.respond_to?(symbol)
311
- raise NoMethodError, "undefined method `#{symbol}' for #{mocha_inspect} which responds like #{@responder.mocha_inspect}"
312
- end
309
+ def method_missing(symbol, *arguments, &block) # rubocop:disable Style/MethodMissingSuper
310
+ check_expiry
311
+ check_responder_responds_to(symbol)
313
312
  invocation = Invocation.new(self, symbol, *arguments, &block)
314
313
  if (matching_expectation_allowing_invocation = all_expectations.match_allowing_invocation(invocation))
315
314
  matching_expectation_allowing_invocation.invoke(invocation)
316
315
  elsif (matching_expectation = all_expectations.match(invocation)) || (!matching_expectation && !@everything_stubbed)
317
- if @unexpected_invocation.nil?
318
- @unexpected_invocation = invocation
319
- matching_expectation.invoke(invocation) if matching_expectation
320
- message = "#{@unexpected_invocation.call_description}\n#{@mockery.mocha_inspect}"
321
- else
322
- message = @unexpected_invocation.short_call_description
323
- end
324
- raise ExpectationErrorFactory.build("unexpected invocation: #{message}", caller)
316
+ raise_unexpected_invocation_error(invocation, matching_expectation)
325
317
  end
326
318
  end
327
- # rubocop:enable Style/MethodMissingSuper,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
328
319
 
329
320
  # @private
330
321
  def respond_to_missing?(symbol, include_private = false)
@@ -339,8 +330,8 @@ module Mocha
339
330
  end
340
331
  end
341
332
 
342
- # @private
343
333
  if PRE_RUBY_V19
334
+ # @private
344
335
  def respond_to?(symbol, include_private = false)
345
336
  respond_to_missing?(symbol, include_private)
346
337
  end
@@ -351,6 +342,11 @@ module Mocha
351
342
  @expectations.verified?(assertion_counter)
352
343
  end
353
344
 
345
+ # @private
346
+ def __expire__
347
+ @expired = true
348
+ end
349
+
354
350
  # @private
355
351
  def mocha_inspect
356
352
  @name.mocha_inspect
@@ -370,5 +366,35 @@ module Mocha
370
366
  def any_expectations?
371
367
  @expectations.any?
372
368
  end
369
+
370
+ private
371
+
372
+ def raise_unexpected_invocation_error(invocation, matching_expectation)
373
+ if @unexpected_invocation.nil?
374
+ @unexpected_invocation = invocation
375
+ matching_expectation.invoke(invocation) if matching_expectation
376
+ message = "#{@unexpected_invocation.call_description}\n#{@mockery.mocha_inspect}"
377
+ else
378
+ message = @unexpected_invocation.short_call_description
379
+ end
380
+ raise ExpectationErrorFactory.build("unexpected invocation: #{message}", caller)
381
+ end
382
+
383
+ def check_responder_responds_to(symbol)
384
+ if @responder && !@responder.respond_to?(symbol) # rubocop:disable Style/GuardClause
385
+ raise NoMethodError, "undefined method `#{symbol}' for #{mocha_inspect} which responds like #{@responder.mocha_inspect}"
386
+ end
387
+ end
388
+
389
+ def check_expiry
390
+ 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
373
399
  end
374
400
  end
data/lib/mocha/mockery.rb CHANGED
@@ -35,12 +35,13 @@ module Mocha
35
35
 
36
36
  class << self
37
37
  def instance
38
- instances.last || Null.new
38
+ @instances.last || Null.new
39
39
  end
40
40
 
41
41
  def setup
42
+ @instances ||= []
42
43
  mockery = new
43
- mockery.logger = instance.logger unless instances.empty?
44
+ mockery.logger = instance.logger unless @instances.empty?
44
45
  @instances.push(mockery)
45
46
  end
46
47
 
@@ -52,13 +53,6 @@ module Mocha
52
53
  instance.teardown
53
54
  ensure
54
55
  @instances.pop
55
- @instances = nil if instances.empty?
56
- end
57
-
58
- private
59
-
60
- def instances
61
- @instances ||= []
62
56
  end
63
57
  end
64
58
 
@@ -92,16 +86,15 @@ module Mocha
92
86
  end
93
87
  raise ExpectationErrorFactory.build(message, backtrace)
94
88
  end
95
- expectations.each do |e|
96
- unless Mocha.configuration.stubbing_method_unnecessarily == :allow
97
- next if e.used?
98
- on_stubbing_method_unnecessarily(e)
99
- 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)
100
92
  end
101
93
  end
102
94
 
103
95
  def teardown
104
96
  stubba.unstub_all
97
+ mocks.each(&:__expire__)
105
98
  reset
106
99
  end
107
100
 
@@ -119,27 +112,23 @@ module Mocha
119
112
 
120
113
  def mocha_inspect
121
114
  message = ''
122
- message << "unsatisfied expectations:\n- #{unsatisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" unless unsatisfied_expectations.empty?
123
- message << "satisfied expectations:\n- #{satisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" unless satisfied_expectations.empty?
124
- message << "states:\n- #{state_machines.map(&:mocha_inspect).join("\n- ")}" unless state_machines.empty?
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?
125
118
  message
126
119
  end
127
120
 
128
121
  def on_stubbing(object, method)
129
122
  method = PRE_RUBY_V19 ? method.to_s : method.to_sym
130
- method_signature = "#{object.mocha_inspect}.#{method}"
131
- check(:stubbing_non_existent_method, 'non-existent method', method_signature) do
123
+ signature_proc = lambda { "#{object.mocha_inspect}.#{method}" }
124
+ check(:stubbing_non_existent_method, 'non-existent method', signature_proc) do
132
125
  !(object.stubba_class.__method_exists__?(method, true) || object.respond_to?(method.to_sym))
133
126
  end
134
- check(:stubbing_non_public_method, 'non-public method', method_signature) do
127
+ check(:stubbing_non_public_method, 'non-public method', signature_proc) do
135
128
  object.stubba_class.__method_exists__?(method, false)
136
129
  end
137
- check(:stubbing_method_on_nil, 'method on nil', method_signature) { object.nil? }
138
- check(:stubbing_method_on_non_mock_object, 'method on non-mock object', method_signature)
139
- end
140
-
141
- def on_stubbing_method_unnecessarily(expectation)
142
- check(:stubbing_method_unnecessarily, 'method unnecessarily', expectation.method_signature, expectation.backtrace)
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)
143
132
  end
144
133
 
145
134
  attr_writer :logger
@@ -150,9 +139,10 @@ module Mocha
150
139
 
151
140
  private
152
141
 
153
- def check(action, description, method_signature, backtrace = caller)
142
+ def check(action, description, signature_proc, backtrace = caller)
154
143
  treatment = Mocha.configuration.send(action)
155
144
  return if (treatment == :allow) || (block_given? && !yield)
145
+ method_signature = signature_proc.call
156
146
  message = "stubbing #{description}: #{method_signature}"
157
147
  raise StubbingError.new(message, backtrace) if treatment == :prevent
158
148
  logger.warn(message) if treatment == :warn
@@ -13,6 +13,7 @@ require 'mocha/parameter_matchers/equals'
13
13
  require 'mocha/parameter_matchers/has_entry'
14
14
  require 'mocha/parameter_matchers/has_entries'
15
15
  require 'mocha/parameter_matchers/has_key'
16
+ require 'mocha/parameter_matchers/has_keys'
16
17
  require 'mocha/parameter_matchers/has_value'
17
18
  require 'mocha/parameter_matchers/includes'
18
19
  require 'mocha/parameter_matchers/instance_of'
@@ -22,11 +22,10 @@ module Mocha
22
22
  # object.expects(:method_1).with(has_entries('key_1' => 1, 'key_2' => 2))
23
23
  # object.method_1('key_1' => 1, 'key_2' => 99)
24
24
  # # error raised, because method_1 was not called with Hash containing entries: 'key_1' => 1, 'key_2' => 2
25
- # rubocop:disable Naming/PredicateName
26
- def has_entries(entries)
25
+ #
26
+ def has_entries(entries) # rubocop:disable Naming/PredicateName
27
27
  HasEntries.new(entries)
28
28
  end
29
- # rubocop:enable Naming/PredicateName
30
29
 
31
30
  # Parameter matcher which matches when actual parameter contains all expected +Hash+ entries.
32
31
  class HasEntries < Base
@@ -39,23 +39,13 @@ module Mocha
39
39
  # object.expects(:method_1).with(has_entry('key_1' => 1))
40
40
  # object.method_1('key_1' => 2, 'key_2' => 1)
41
41
  # # error raised, because method_1 was not called with Hash containing entry: 'key_1' => 1
42
- # rubocop:disable Naming/PredicateName
43
- def has_entry(*options)
42
+ #
43
+ def has_entry(*options) # rubocop:disable Naming/PredicateName
44
44
  case options.length
45
+ when 0
46
+ raise ArgumentError, 'No arguments. Expecting at least one.'
45
47
  when 1
46
- case options[0]
47
- when Hash
48
- case options[0].length
49
- when 0
50
- raise ArgumentError, 'Argument has no entries.'
51
- when 1
52
- key, value = options[0].first
53
- else
54
- raise ArgumentError, 'Argument has multiple entries. Use Mocha::ParameterMatchers#has_entries instead.'
55
- end
56
- else
57
- raise ArgumentError, 'Argument is not a Hash.'
58
- end
48
+ key, value = parse_option(options[0])
59
49
  when 2
60
50
  key, value = options
61
51
  else
@@ -63,7 +53,6 @@ module Mocha
63
53
  end
64
54
  HasEntry.new(key, value)
65
55
  end
66
- # rubocop:enable Naming/PredicateName
67
56
 
68
57
  # Parameter matcher which matches when actual parameter contains expected +Hash+ entry.
69
58
  class HasEntry < Base
@@ -86,5 +75,24 @@ module Mocha
86
75
  "has_entry(#{@key.mocha_inspect} => #{@value.mocha_inspect})"
87
76
  end
88
77
  end
78
+
79
+ private
80
+
81
+ # @private
82
+ def parse_option(option)
83
+ case option
84
+ when Hash
85
+ case option.length
86
+ when 0
87
+ raise ArgumentError, 'Argument has no entries.'
88
+ when 1
89
+ option.first
90
+ else
91
+ raise ArgumentError, 'Argument has multiple entries. Use Mocha::ParameterMatchers#has_entries instead.'
92
+ end
93
+ else
94
+ raise ArgumentError, 'Argument is not a Hash.'
95
+ end
96
+ end
89
97
  end
90
98
  end
@@ -20,11 +20,10 @@ module Mocha
20
20
  # object.expects(:method_1).with(has_key('key_1'))
21
21
  # object.method_1('key_2' => 2)
22
22
  # # error raised, because method_1 was not called with Hash containing key: 'key_1'
23
- # rubocop:disable Naming/PredicateName
24
- def has_key(key)
23
+ #
24
+ def has_key(key) # rubocop:disable Naming/PredicateName
25
25
  HasKey.new(key)
26
26
  end
27
- # rubocop:enable Naming/PredicateName
28
27
 
29
28
  # Parameter matcher which matches when actual parameter contains +Hash+ entry with expected key.
30
29
  class HasKey < Base
@@ -0,0 +1,53 @@
1
+ require 'mocha/parameter_matchers/base'
2
+
3
+ module Mocha
4
+ module ParameterMatchers
5
+ # Matches +Hash+ containing +keys+.
6
+ #
7
+ # @param [*Array<Object>] keys expected keys.
8
+ # @return [HasKeys] parameter matcher.
9
+ #
10
+ # @see Expectation#with
11
+ #
12
+ # @example Actual parameter contains entry with expected keys.
13
+ # object = mock()
14
+ # object.expects(:method_1).with(has_keys(:key_1, :key_2))
15
+ # object.method_1(:key_1 => 1, :key_2 => 2, :key_3 => 3)
16
+ # # no error raised
17
+ #
18
+ # @example Actual parameter does not contain all expected keys.
19
+ # object = mock()
20
+ # object.expects(:method_1).with(has_keys(:key_1, :key_2))
21
+ # object.method_1(:key_2 => 2)
22
+ # # error raised, because method_1 was not called with Hash containing key: :key_1
23
+ #
24
+ def has_keys(*keys) # rubocop:disable Naming/PredicateName
25
+ HasKeys.new(*keys)
26
+ end
27
+
28
+ # Parameter matcher which matches when actual parameter contains +Hash+ with all expected keys.
29
+ class HasKeys < Base
30
+ # @private
31
+ def initialize(*keys)
32
+ raise ArgumentError, 'No arguments. Expecting at least one.' if keys.empty?
33
+
34
+ @keys = keys
35
+ end
36
+
37
+ # @private
38
+ def matches?(available_parameters)
39
+ parameter = available_parameters.shift
40
+ return false unless parameter.respond_to?(:keys)
41
+
42
+ @keys.map(&:to_matcher).all? do |matcher|
43
+ parameter.keys.any? { |key| matcher.matches?([key]) }
44
+ end
45
+ end
46
+
47
+ # @private
48
+ def mocha_inspect
49
+ "has_keys(#{@keys.mocha_inspect(false)})"
50
+ end
51
+ end
52
+ end
53
+ end
@@ -20,11 +20,10 @@ module Mocha
20
20
  # object.expects(:method_1).with(has_value(1))
21
21
  # object.method_1('key_2' => 2)
22
22
  # # error raised, because method_1 was not called with Hash containing value: 1
23
- # rubocop:disable Naming/PredicateName
24
- def has_value(value)
23
+ #
24
+ def has_value(value) # rubocop:disable Naming/PredicateName
25
25
  HasValue.new(value)
26
26
  end
27
- # rubocop:enable Naming/PredicateName
28
27
 
29
28
  # Parameter matcher which matches when actual parameter contains +Hash+ entry with expected value.
30
29
  class HasValue < Base
@@ -21,11 +21,10 @@ module Mocha
21
21
  # object.expects(:method_1).with(is_a(Integer))
22
22
  # object.method_1('string')
23
23
  # # error raised, because method_1 was not called with an Integer
24
- # rubocop:disable Naming/PredicateName
25
- def is_a(klass)
24
+ #
25
+ def is_a(klass) # rubocop:disable Naming/PredicateName
26
26
  IsA.new(klass)
27
27
  end
28
- # rubocop:enable Naming/PredicateName
29
28
 
30
29
  # Parameter matcher which matches when actual parameter is a specific class.
31
30
  class IsA < Base
@@ -20,11 +20,10 @@ module Mocha
20
20
  # object.expects(:method_1).with(Not(includes(1)))
21
21
  # object.method_1([0, 1, 2, 3])
22
22
  # # error raised, because method_1 was not called with object not including 1
23
- # rubocop:disable Naming/MethodName
24
- def Not(matcher)
23
+ #
24
+ def Not(matcher) # rubocop:disable Naming/MethodName
25
25
  Not.new(matcher)
26
26
  end
27
- # rubocop:enable Naming/MethodName
28
27
 
29
28
  # Parameter matcher which inverts the logic of the specified matcher using a logical NOT operation.
30
29
  class Not < Base