mocha 0.5.6 → 3.0.2

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 (192) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/.github/FUNDING.yml +1 -0
  4. data/.rubocop.yml +92 -0
  5. data/.rubocop_todo.yml +39 -0
  6. data/.yardopts +25 -0
  7. data/CONTRIBUTING.md +7 -0
  8. data/COPYING.md +3 -0
  9. data/Gemfile +17 -0
  10. data/{MIT-LICENSE → MIT-LICENSE.md} +2 -2
  11. data/README.md +361 -0
  12. data/RELEASE.md +1235 -0
  13. data/Rakefile +165 -123
  14. data/gemfiles/Gemfile.minitest.latest +8 -0
  15. data/gemfiles/Gemfile.rubocop +9 -0
  16. data/gemfiles/Gemfile.test-unit.latest +8 -0
  17. data/lib/mocha/any_instance_method.rb +12 -26
  18. data/lib/mocha/any_instance_receiver.rb +20 -0
  19. data/lib/mocha/api.rb +213 -0
  20. data/lib/mocha/argument_iterator.rb +17 -0
  21. data/lib/mocha/backtrace_filter.rb +15 -0
  22. data/lib/mocha/block_matchers.rb +33 -0
  23. data/lib/mocha/cardinality.rb +110 -0
  24. data/lib/mocha/central.rb +33 -22
  25. data/lib/mocha/change_state_side_effect.rb +17 -0
  26. data/lib/mocha/class_methods.rb +67 -0
  27. data/lib/mocha/configuration.rb +338 -0
  28. data/lib/mocha/default_name.rb +15 -0
  29. data/lib/mocha/default_receiver.rb +13 -0
  30. data/lib/mocha/deprecation.rb +19 -14
  31. data/lib/mocha/detection/minitest.rb +25 -0
  32. data/lib/mocha/detection/test_unit.rb +30 -0
  33. data/lib/mocha/error_with_filtered_backtrace.rb +15 -0
  34. data/lib/mocha/exception_raiser.rb +11 -10
  35. data/lib/mocha/expectation.rb +553 -168
  36. data/lib/mocha/expectation_error.rb +9 -14
  37. data/lib/mocha/expectation_error_factory.rb +37 -0
  38. data/lib/mocha/expectation_list.rb +30 -14
  39. data/lib/mocha/hooks.rb +55 -0
  40. data/lib/mocha/ignoring_warning.rb +20 -0
  41. data/lib/mocha/impersonating_any_instance_name.rb +13 -0
  42. data/lib/mocha/impersonating_name.rb +13 -0
  43. data/lib/mocha/in_state_ordering_constraint.rb +17 -0
  44. data/lib/mocha/inspect.rb +56 -22
  45. data/lib/mocha/instance_method.rb +17 -4
  46. data/lib/mocha/integration/assertion_counter.rb +15 -0
  47. data/lib/mocha/integration/minitest/adapter.rb +71 -0
  48. data/lib/mocha/integration/minitest.rb +29 -0
  49. data/lib/mocha/integration/monkey_patcher.rb +26 -0
  50. data/lib/mocha/integration/test_unit/adapter.rb +61 -0
  51. data/lib/mocha/integration/test_unit.rb +29 -0
  52. data/lib/mocha/integration.rb +5 -0
  53. data/lib/mocha/invocation.rb +76 -0
  54. data/lib/mocha/logger.rb +13 -0
  55. data/lib/mocha/macos_version.rb +7 -0
  56. data/lib/mocha/method_matcher.rb +8 -10
  57. data/lib/mocha/minitest.rb +7 -0
  58. data/lib/mocha/mock.rb +333 -108
  59. data/lib/mocha/mockery.rb +199 -0
  60. data/lib/mocha/name.rb +13 -0
  61. data/lib/mocha/not_initialized_error.rb +9 -0
  62. data/lib/mocha/object_methods.rb +183 -0
  63. data/lib/mocha/object_receiver.rb +20 -0
  64. data/lib/mocha/parameter_matchers/all_of.rb +38 -28
  65. data/lib/mocha/parameter_matchers/any_of.rb +44 -33
  66. data/lib/mocha/parameter_matchers/any_parameters.rb +33 -26
  67. data/lib/mocha/parameter_matchers/anything.rb +31 -22
  68. data/lib/mocha/parameter_matchers/base_methods.rb +64 -0
  69. data/lib/mocha/parameter_matchers/equals.rb +36 -25
  70. data/lib/mocha/parameter_matchers/equivalent_uri.rb +65 -0
  71. data/lib/mocha/parameter_matchers/has_entries.rb +48 -29
  72. data/lib/mocha/parameter_matchers/has_entry.rb +90 -42
  73. data/lib/mocha/parameter_matchers/has_key.rb +39 -26
  74. data/lib/mocha/parameter_matchers/has_keys.rb +59 -0
  75. data/lib/mocha/parameter_matchers/has_value.rb +39 -26
  76. data/lib/mocha/parameter_matchers/includes.rb +88 -23
  77. data/lib/mocha/parameter_matchers/instance_methods.rb +28 -0
  78. data/lib/mocha/parameter_matchers/instance_of.rb +37 -26
  79. data/lib/mocha/parameter_matchers/is_a.rb +38 -26
  80. data/lib/mocha/parameter_matchers/kind_of.rb +39 -26
  81. data/lib/mocha/parameter_matchers/not.rb +37 -26
  82. data/lib/mocha/parameter_matchers/optionally.rb +52 -17
  83. data/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb +91 -0
  84. data/lib/mocha/parameter_matchers/regexp_matches.rb +37 -25
  85. data/lib/mocha/parameter_matchers/responds_with.rb +82 -0
  86. data/lib/mocha/parameter_matchers/yaml_equivalent.rb +55 -0
  87. data/lib/mocha/parameter_matchers.rb +12 -5
  88. data/lib/mocha/parameters_matcher.rb +28 -19
  89. data/lib/mocha/raised_exception.rb +13 -0
  90. data/lib/mocha/return_values.rb +13 -18
  91. data/lib/mocha/ruby_version.rb +7 -0
  92. data/lib/mocha/sequence.rb +23 -17
  93. data/lib/mocha/single_return_value.rb +8 -18
  94. data/lib/mocha/state_machine.rb +95 -0
  95. data/lib/mocha/stubbed_method.rb +96 -0
  96. data/lib/mocha/stubbing_error.rb +10 -0
  97. data/lib/mocha/test_unit.rb +7 -0
  98. data/lib/mocha/thrower.rb +15 -0
  99. data/lib/mocha/thrown_object.rb +14 -0
  100. data/lib/mocha/version.rb +5 -0
  101. data/lib/mocha/yield_parameters.rb +12 -20
  102. data/lib/mocha.rb +19 -17
  103. data/mise.toml +2 -0
  104. data/mocha.gemspec +40 -0
  105. metadata +130 -145
  106. data/COPYING +0 -3
  107. data/README +0 -35
  108. data/RELEASE +0 -188
  109. data/examples/misc.rb +0 -44
  110. data/examples/mocha.rb +0 -26
  111. data/examples/stubba.rb +0 -65
  112. data/lib/mocha/auto_verify.rb +0 -118
  113. data/lib/mocha/class_method.rb +0 -66
  114. data/lib/mocha/infinite_range.rb +0 -25
  115. data/lib/mocha/is_a.rb +0 -9
  116. data/lib/mocha/metaclass.rb +0 -7
  117. data/lib/mocha/missing_expectation.rb +0 -17
  118. data/lib/mocha/multiple_yields.rb +0 -20
  119. data/lib/mocha/no_yields.rb +0 -11
  120. data/lib/mocha/object.rb +0 -110
  121. data/lib/mocha/parameter_matchers/base.rb +0 -15
  122. data/lib/mocha/parameter_matchers/object.rb +0 -9
  123. data/lib/mocha/pretty_parameters.rb +0 -28
  124. data/lib/mocha/setup_and_teardown.rb +0 -23
  125. data/lib/mocha/single_yield.rb +0 -18
  126. data/lib/mocha/standalone.rb +0 -32
  127. data/lib/mocha/stub.rb +0 -18
  128. data/lib/mocha/test_case_adapter.rb +0 -49
  129. data/lib/mocha_standalone.rb +0 -2
  130. data/lib/stubba.rb +0 -2
  131. data/test/acceptance/expected_invocation_count_acceptance_test.rb +0 -187
  132. data/test/acceptance/mocha_acceptance_test.rb +0 -98
  133. data/test/acceptance/mock_with_initializer_block_acceptance_test.rb +0 -44
  134. data/test/acceptance/mocked_methods_dispatch_acceptance_test.rb +0 -71
  135. data/test/acceptance/optional_parameters_acceptance_test.rb +0 -63
  136. data/test/acceptance/parameter_matcher_acceptance_test.rb +0 -117
  137. data/test/acceptance/partial_mocks_acceptance_test.rb +0 -40
  138. data/test/acceptance/sequence_acceptance_test.rb +0 -179
  139. data/test/acceptance/standalone_acceptance_test.rb +0 -131
  140. data/test/acceptance/stubba_acceptance_test.rb +0 -102
  141. data/test/active_record_test_case.rb +0 -36
  142. data/test/deprecation_disabler.rb +0 -15
  143. data/test/execution_point.rb +0 -34
  144. data/test/integration/mocha_test_result_integration_test.rb +0 -105
  145. data/test/integration/stubba_integration_test.rb +0 -89
  146. data/test/integration/stubba_test_result_integration_test.rb +0 -85
  147. data/test/method_definer.rb +0 -18
  148. data/test/test_helper.rb +0 -12
  149. data/test/test_runner.rb +0 -31
  150. data/test/unit/any_instance_method_test.rb +0 -126
  151. data/test/unit/array_inspect_test.rb +0 -16
  152. data/test/unit/auto_verify_test.rb +0 -129
  153. data/test/unit/central_test.rb +0 -124
  154. data/test/unit/class_method_test.rb +0 -200
  155. data/test/unit/date_time_inspect_test.rb +0 -21
  156. data/test/unit/expectation_error_test.rb +0 -24
  157. data/test/unit/expectation_list_test.rb +0 -75
  158. data/test/unit/expectation_raiser_test.rb +0 -28
  159. data/test/unit/expectation_test.rb +0 -483
  160. data/test/unit/hash_inspect_test.rb +0 -16
  161. data/test/unit/infinite_range_test.rb +0 -53
  162. data/test/unit/metaclass_test.rb +0 -22
  163. data/test/unit/method_matcher_test.rb +0 -23
  164. data/test/unit/missing_expectation_test.rb +0 -42
  165. data/test/unit/mock_test.rb +0 -323
  166. data/test/unit/multiple_yields_test.rb +0 -18
  167. data/test/unit/no_yield_test.rb +0 -18
  168. data/test/unit/object_inspect_test.rb +0 -37
  169. data/test/unit/object_test.rb +0 -165
  170. data/test/unit/parameter_matchers/all_of_test.rb +0 -26
  171. data/test/unit/parameter_matchers/any_of_test.rb +0 -26
  172. data/test/unit/parameter_matchers/anything_test.rb +0 -21
  173. data/test/unit/parameter_matchers/has_entries_test.rb +0 -30
  174. data/test/unit/parameter_matchers/has_entry_test.rb +0 -40
  175. data/test/unit/parameter_matchers/has_key_test.rb +0 -25
  176. data/test/unit/parameter_matchers/has_value_test.rb +0 -25
  177. data/test/unit/parameter_matchers/includes_test.rb +0 -25
  178. data/test/unit/parameter_matchers/instance_of_test.rb +0 -25
  179. data/test/unit/parameter_matchers/is_a_test.rb +0 -25
  180. data/test/unit/parameter_matchers/kind_of_test.rb +0 -25
  181. data/test/unit/parameter_matchers/not_test.rb +0 -26
  182. data/test/unit/parameter_matchers/regexp_matches_test.rb +0 -25
  183. data/test/unit/parameter_matchers/stub_matcher.rb +0 -23
  184. data/test/unit/parameters_matcher_test.rb +0 -121
  185. data/test/unit/return_values_test.rb +0 -63
  186. data/test/unit/sequence_test.rb +0 -104
  187. data/test/unit/setup_and_teardown_test.rb +0 -76
  188. data/test/unit/single_return_value_test.rb +0 -33
  189. data/test/unit/single_yield_test.rb +0 -18
  190. data/test/unit/string_inspect_test.rb +0 -11
  191. data/test/unit/stub_test.rb +0 -24
  192. data/test/unit/yield_parameters_test.rb +0 -93
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mocha
4
+ module BlockMatchers
5
+ class OptionalBlock
6
+ def match?(_actual_block)
7
+ true
8
+ end
9
+
10
+ def mocha_inspect; end
11
+ end
12
+
13
+ class BlockGiven
14
+ def match?(actual_block)
15
+ !actual_block.nil?
16
+ end
17
+
18
+ def mocha_inspect
19
+ 'with block given'
20
+ end
21
+ end
22
+
23
+ class NoBlockGiven
24
+ def match?(actual_block)
25
+ actual_block.nil?
26
+ end
27
+
28
+ def mocha_inspect
29
+ 'with no block given'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mocha
4
+ class Cardinality
5
+ INFINITY = 1 / 0.0
6
+
7
+ def initialize(required = 0, maximum = INFINITY)
8
+ update(required, maximum)
9
+ @invocations = []
10
+ end
11
+
12
+ def exactly(count)
13
+ update(count, count)
14
+ end
15
+
16
+ def at_least(count)
17
+ update(count, INFINITY)
18
+ end
19
+
20
+ def at_most(count)
21
+ update(0, count)
22
+ end
23
+
24
+ def times(range_or_count)
25
+ case range_or_count
26
+ when Range then update(range_or_count.first, range_or_count.last)
27
+ else update(range_or_count, range_or_count)
28
+ end
29
+ end
30
+
31
+ def <<(invocation)
32
+ @invocations << invocation
33
+ end
34
+
35
+ def invocations_allowed?
36
+ @invocations.size < maximum
37
+ end
38
+
39
+ def invocations_never_allowed?
40
+ maximum.zero?
41
+ end
42
+
43
+ def satisfied?
44
+ @invocations.size >= required
45
+ end
46
+
47
+ def needs_verifying?
48
+ !allowed_any_number_of_times?
49
+ end
50
+
51
+ def verified?
52
+ (@invocations.size >= required) && (@invocations.size <= maximum)
53
+ end
54
+
55
+ def allowed_any_number_of_times?
56
+ required.zero? && infinite?(maximum)
57
+ end
58
+
59
+ def used?
60
+ @invocations.any? || maximum.zero?
61
+ end
62
+
63
+ def anticipated_times
64
+ if allowed_any_number_of_times?
65
+ 'allowed any number of times'
66
+ elsif required.zero? && maximum.zero?
67
+ "expected #{count(maximum)}"
68
+ elsif required == maximum
69
+ "expected exactly #{count(required)}"
70
+ elsif infinite?(maximum)
71
+ "expected at least #{count(required)}"
72
+ elsif required.zero?
73
+ "expected at most #{count(maximum)}"
74
+ else
75
+ "expected between #{required} and #{count(maximum)}"
76
+ end
77
+ end
78
+
79
+ def invoked_times
80
+ "invoked #{count(@invocations.size)}"
81
+ end
82
+
83
+ def actual_invocations
84
+ @invocations.map(&:full_description).join
85
+ end
86
+
87
+ protected
88
+
89
+ attr_reader :required, :maximum
90
+
91
+ def count(number)
92
+ case number
93
+ when 0 then 'never'
94
+ when 1 then 'once'
95
+ when 2 then 'twice'
96
+ else "#{number} times"
97
+ end
98
+ end
99
+
100
+ def update(required, maximum)
101
+ @required = required
102
+ @maximum = maximum
103
+ self
104
+ end
105
+
106
+ def infinite?(number)
107
+ number.respond_to?(:infinite?) && number.infinite?
108
+ end
109
+ end
110
+ end
data/lib/mocha/central.rb CHANGED
@@ -1,35 +1,46 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Mocha
2
-
3
4
  class Central
4
-
5
+ class Null < self
6
+ def initialize(&block)
7
+ super
8
+ @raise_not_initialized_error = block
9
+ end
10
+
11
+ def stub(*)
12
+ @raise_not_initialized_error.call
13
+ end
14
+
15
+ def unstub(*)
16
+ @raise_not_initialized_error.call
17
+ end
18
+ end
19
+
5
20
  attr_accessor :stubba_methods
6
-
21
+
7
22
  def initialize
8
23
  self.stubba_methods = []
9
24
  end
10
-
25
+
11
26
  def stub(method)
12
- unless stubba_methods.include?(method)
13
- method.stub
14
- stubba_methods.push method
15
- end
16
- end
17
-
18
- def verify_all(&block)
19
- unique_mocks.each { |mock| mock.verify(&block) }
27
+ return if stubba_methods.detect { |m| m.matches?(method) }
28
+
29
+ method.stub
30
+ stubba_methods.push(method)
20
31
  end
21
-
22
- def unique_mocks
23
- stubba_methods.inject({}) { |mocks, method| mocks[method.mock.__id__] = method.mock; mocks }.values
32
+
33
+ def unstub(method)
34
+ return unless (existing = stubba_methods.detect { |m| m.matches?(method) })
35
+
36
+ existing.unstub
37
+ stubba_methods.delete(existing)
24
38
  end
25
-
39
+
26
40
  def unstub_all
27
- while stubba_methods.length > 0
28
- method = stubba_methods.pop
29
- method.unstub
41
+ while stubba_methods.any?
42
+ unstub(stubba_methods.first)
30
43
  end
31
44
  end
32
-
33
45
  end
34
-
35
- end
46
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mocha
4
+ class ChangeStateSideEffect
5
+ def initialize(state)
6
+ @state = state
7
+ end
8
+
9
+ def perform
10
+ @state.activate
11
+ end
12
+
13
+ def mocha_inspect
14
+ "then #{@state.mocha_inspect}"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mocha/mockery'
4
+ require 'mocha/any_instance_method'
5
+
6
+ module Mocha
7
+ # Methods added to all classes to allow mocking and stubbing on real (i.e. non-mock) objects.
8
+ module ClassMethods
9
+ # @private
10
+ class AnyInstance
11
+ def initialize(klass)
12
+ @stubba_object = klass
13
+ end
14
+
15
+ def mocha(instantiate: true)
16
+ if instantiate
17
+ @mocha ||= Mocha::Mockery.instance.mock_impersonating_any_instance_of(@stubba_object)
18
+ else
19
+ defined?(@mocha) ? @mocha : nil
20
+ end
21
+ end
22
+
23
+ def stubba_method
24
+ Mocha::AnyInstanceMethod
25
+ end
26
+
27
+ def stubba_class
28
+ @stubba_object
29
+ end
30
+
31
+ def stubba_respond_to?(symbol)
32
+ @stubba_object.allocate.respond_to?(symbol.to_sym)
33
+ end
34
+
35
+ attr_reader :stubba_object
36
+ end
37
+
38
+ # @return [Mock] a mock object which will detect calls to any instance of this class.
39
+ # @raise [StubbingError] if attempting to stub method which is not allowed.
40
+ #
41
+ # @example Return false to invocation of +Product#save+ for any instance of +Product+.
42
+ # Product.any_instance.stubs(:save).returns(false)
43
+ # product_1 = Product.new
44
+ # assert_equal false, product_1.save
45
+ # product_2 = Product.new
46
+ # assert_equal false, product_2.save
47
+ def any_instance
48
+ if frozen?
49
+ raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}.any_instance", caller)
50
+ end
51
+
52
+ @any_instance ||= AnyInstance.new(self)
53
+ end
54
+
55
+ # @private
56
+ def __method_visibility__(method, include_public_methods: true)
57
+ if include_public_methods && public_method_defined?(method)
58
+ :public
59
+ elsif protected_method_defined?(method)
60
+ :protected
61
+ else
62
+ private_method_defined?(method) ? :private : nil
63
+ end
64
+ end
65
+ alias_method :__method_exists__?, :__method_visibility__
66
+ end
67
+ end
@@ -0,0 +1,338 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mocha/ruby_version'
4
+ require 'mocha/deprecation'
5
+
6
+ module Mocha
7
+ # Allows setting of configuration options. See {Configuration} for the available options.
8
+ #
9
+ # Typically the configuration is set globally in a +test_helper.rb+ or +spec_helper.rb+ file.
10
+ #
11
+ # @see Configuration
12
+ #
13
+ # @yieldparam configuration [Configuration] the configuration for modification
14
+ #
15
+ # @example Setting multiple configuration options
16
+ # Mocha.configure do |c|
17
+ # c.stubbing_method_unnecessarily = :prevent
18
+ # c.stubbing_method_on_non_mock_object = :warn
19
+ # end
20
+ #
21
+ def self.configure
22
+ yield configuration
23
+ end
24
+
25
+ # @private
26
+ def self.configuration
27
+ Configuration.configuration
28
+ end
29
+
30
+ # This class provides a number of ways to configure the library.
31
+ #
32
+ # Typically the configuration is set globally in a +test_helper.rb+ or +spec_helper.rb+ file.
33
+ #
34
+ # @example Setting multiple configuration options
35
+ # Mocha.configure do |c|
36
+ # c.stubbing_method_unnecessarily = :prevent
37
+ # c.stubbing_method_on_non_mock_object = :warn
38
+ # end
39
+ #
40
+ class Configuration
41
+ # @private
42
+ DEFAULTS = {
43
+ stubbing_method_unnecessarily: :allow,
44
+ stubbing_method_on_non_mock_object: :allow,
45
+ stubbing_non_existent_method: :allow,
46
+ stubbing_non_public_method: :allow,
47
+ display_matching_invocations_on_failure: false,
48
+ strict_keyword_argument_matching: Mocha::RUBY_V30_PLUS
49
+ }.freeze
50
+
51
+ attr_reader :options
52
+ protected :options
53
+
54
+ # @private
55
+ def initialize(options = {})
56
+ @options = DEFAULTS.merge(options)
57
+ end
58
+
59
+ # @private
60
+ def initialize_copy(other)
61
+ @options = other.options.dup
62
+ end
63
+
64
+ # @private
65
+ def merge(other)
66
+ self.class.new(@options.merge(other.options))
67
+ end
68
+
69
+ # Configure whether stubbing methods unnecessarily is allowed.
70
+ #
71
+ # This is useful for identifying unused stubs. Unused stubs are often accidentally introduced when code is {http://martinfowler.com/bliki/DefinitionOfRefactoring.html refactored}.
72
+ #
73
+ # When +value+ is +:allow+, do nothing. This is the default.
74
+ # When +value+ is +:warn+, display a warning.
75
+ # When +value+ is +:prevent+, raise a {StubbingError}.
76
+ #
77
+ # @param [Symbol] value one of +:allow+, +:warn+, +:prevent+.
78
+ #
79
+ # @example Preventing unnecessary stubbing of a method
80
+ # Mocha.configure do |c|
81
+ # c.stubbing_method_unnecessarily = :prevent
82
+ # end
83
+ #
84
+ # example = mock('example')
85
+ # example.stubs(:unused_stub)
86
+ # # => Mocha::StubbingError: stubbing method unnecessarily:
87
+ # # => #<Mock:example>.unused_stub(any_parameters)
88
+ #
89
+ def stubbing_method_unnecessarily=(value)
90
+ @options[:stubbing_method_unnecessarily] = value
91
+ end
92
+
93
+ # @private
94
+ def stubbing_method_unnecessarily
95
+ @options[:stubbing_method_unnecessarily]
96
+ end
97
+
98
+ # Configure whether stubbing methods on non-mock objects is allowed.
99
+ #
100
+ # If you like the idea of {http://www.jmock.org/oopsla2004.pdf mocking roles not objects} and {http://www.mockobjects.com/2007/04/test-smell-mocking-concrete-classes.html you don't like stubbing concrete classes}, this is the setting for you. However, while this restriction makes a lot of sense in Java with its {http://java.sun.com/docs/books/tutorial/java/concepts/interface.html explicit interfaces}, it may be moot in Ruby where roles are probably best represented as Modules.
101
+ #
102
+ # When +value+ is +:allow+, do nothing. This is the default.
103
+ # When +value+ is +:warn+, display a warning.
104
+ # When +value+ is +:prevent+, raise a {StubbingError}.
105
+ #
106
+ # @param [Symbol] value one of +:allow+, +:warn+, +:prevent+.
107
+ #
108
+ # @example Preventing stubbing of a method on a non-mock object
109
+ # Mocha.configure do |c|
110
+ # c.stubbing_method_on_non_mock_object = :prevent
111
+ # end
112
+ #
113
+ # class Example
114
+ # def example_method; end
115
+ # end
116
+ #
117
+ # example = Example.new
118
+ # example.stubs(:example_method)
119
+ # # => Mocha::StubbingError: stubbing method on non-mock object:
120
+ # # => #<Example:0x593620>.example_method
121
+ #
122
+ def stubbing_method_on_non_mock_object=(value)
123
+ @options[:stubbing_method_on_non_mock_object] = value
124
+ end
125
+
126
+ # @private
127
+ def stubbing_method_on_non_mock_object
128
+ @options[:stubbing_method_on_non_mock_object]
129
+ end
130
+
131
+ # Configure whether stubbing of non-existent methods is allowed.
132
+ #
133
+ # This is useful if you want to ensure that methods you're mocking really exist. A common criticism of unit tests with mock objects is that such a test may (incorrectly) pass when an equivalent non-mocking test would (correctly) fail. While you should always have some integration tests, particularly for critical business functionality, this Mocha configuration setting should catch scenarios when mocked methods and real methods have become misaligned.
134
+ #
135
+ # When +value+ is +:allow+, do nothing. This is the default.
136
+ # When +value+ is +:warn+, display a warning.
137
+ # When +value+ is +:prevent+, raise a {StubbingError}.
138
+ #
139
+ # @param [Symbol] value one of +:allow+, +:warn+, +:prevent+.
140
+ #
141
+ # @example Preventing stubbing of a non-existent method
142
+ #
143
+ # Mocha.configure do |c|
144
+ # c.stubbing_non_existent_method = :prevent
145
+ # end
146
+ #
147
+ # class Example
148
+ # end
149
+ #
150
+ # example = Example.new
151
+ # example.stubs(:method_that_doesnt_exist)
152
+ # # => Mocha::StubbingError: stubbing non-existent method:
153
+ # # => #<Example:0x593760>.method_that_doesnt_exist
154
+ #
155
+ def stubbing_non_existent_method=(value)
156
+ @options[:stubbing_non_existent_method] = value
157
+ end
158
+
159
+ # @private
160
+ def stubbing_non_existent_method
161
+ @options[:stubbing_non_existent_method]
162
+ end
163
+
164
+ # Configure whether stubbing of non-public methods is allowed.
165
+ #
166
+ # Many people think that it's good practice only to mock public methods. This is one way to prevent your tests being too tightly coupled to the internal implementation of a class. Such tests tend to be very brittle and not much use when refactoring.
167
+ #
168
+ # When +value+ is +:allow+, do nothing. This is the default.
169
+ # When +value+ is +:warn+, display a warning.
170
+ # When +value+ is +:prevent+, raise a {StubbingError}.
171
+ #
172
+ # @param [Symbol] value one of +:allow+, +:warn+, +:prevent+.
173
+ #
174
+ # @example Preventing stubbing of a non-public method
175
+ # Mocha.configure do |c|
176
+ # c.stubbing_non_public_method = :prevent
177
+ # end
178
+ #
179
+ # class Example
180
+ # def internal_method; end
181
+ # private :internal_method
182
+ # end
183
+ #
184
+ # example = Example.new
185
+ # example.stubs(:internal_method)
186
+ # # => Mocha::StubbingError: stubbing non-public method:
187
+ # # => #<Example:0x593530>.internal_method
188
+ #
189
+ def stubbing_non_public_method=(value)
190
+ @options[:stubbing_non_public_method] = value
191
+ end
192
+
193
+ # @private
194
+ def stubbing_non_public_method
195
+ @options[:stubbing_non_public_method]
196
+ end
197
+
198
+ # Display matching invocations alongside expectations on Mocha-related test failure.
199
+ #
200
+ # @param [Boolean] value +true+ to enable display of matching invocations; disabled by default.
201
+ #
202
+ # @example Enable display of matching invocations
203
+ # Mocha.configure do |c|
204
+ # c.display_matching_invocations_on_failure = true
205
+ # end
206
+ #
207
+ # foo = mock('foo')
208
+ # foo.expects(:bar)
209
+ # foo.stubs(:baz).returns('baz').raises(RuntimeError).throws(:tag, 'value')
210
+ #
211
+ # foo.baz(1, 2)
212
+ # assert_raises(RuntimeError) { foo.baz(3, 4) }
213
+ # assert_throws(:tag) { foo.baz(5, 6) }
214
+ #
215
+ # not all expectations were satisfied
216
+ # unsatisfied expectations:
217
+ # - expected exactly once, invoked never: #<Mock:foo>.bar
218
+ # satisfied expectations:
219
+ # - allowed any number of times, invoked 3 times: #<Mock:foo>.baz(any_parameters)
220
+ # - #<Mock:foo>.baz(1, 2) # => "baz"
221
+ # - #<Mock:foo>.baz(3, 4) # => raised RuntimeError
222
+ # - #<Mock:foo>.baz(5, 6) # => threw (:tag, "value")
223
+ def display_matching_invocations_on_failure=(value)
224
+ @options[:display_matching_invocations_on_failure] = value
225
+ end
226
+
227
+ # @private
228
+ def display_matching_invocations_on_failure?
229
+ @options[:display_matching_invocations_on_failure]
230
+ end
231
+
232
+ # Perform strict keyword argument comparison. Only supported in Ruby >= v2.7.
233
+ #
234
+ # When this option is set to +false+ a positional +Hash+ and a set of keyword arguments are treated the same during comparison, which can lead to misleading passing tests in Ruby >= v3.0 (see examples below). However, a deprecation warning will be displayed if a positional +Hash+ matches a set of keyword arguments or vice versa.
235
+ #
236
+ # For more details on keyword arguments in Ruby v3, refer to {https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0 this article}.
237
+ #
238
+ # Note that +Hash+-related matchers such as {ParameterMatchers::Methods#has_value} or {ParameterMatchers::Methods#has_key} will still treat a positional +Hash+ and a set of keyword arguments the same, so misleading passing tests are still possible when they are used.
239
+ #
240
+ # This configuration option is +false+ by default in Ruby v2.7 to enable gradual adoption, but +true+ by default in Ruby >= v3.0.
241
+ #
242
+ # @param [Boolean] value +true+ to enable strict keyword argument matching.
243
+ #
244
+ # @example Loose keyword argument matching (default in Ruby v2.7)
245
+ #
246
+ # Mocha.configure do |c|
247
+ # c.strict_keyword_argument_matching = false
248
+ # end
249
+ #
250
+ # class Example
251
+ # def foo(a, bar:); end
252
+ # end
253
+ #
254
+ # example = Example.new
255
+ # example.expects(:foo).with('a', bar: 'b')
256
+ # example.foo('a', { bar: 'b' })
257
+ # # This passes the test, but would result in an ArgumentError in practice
258
+ #
259
+ # @example Strict keyword argument matching (default in Ruby >= v3.0)
260
+ #
261
+ # Mocha.configure do |c|
262
+ # c.strict_keyword_argument_matching = true
263
+ # end
264
+ #
265
+ # class Example
266
+ # def foo(a, bar:); end
267
+ # end
268
+ #
269
+ # example = Example.new
270
+ # example.expects(:foo).with('a', bar: 'b')
271
+ # example.foo('a', { bar: 'b' })
272
+ # # This now fails as expected
273
+ def strict_keyword_argument_matching=(value)
274
+ raise 'Strict keyword argument matching requires Ruby 2.7 and above.' unless Mocha::RUBY_V27_PLUS
275
+
276
+ @options[:strict_keyword_argument_matching] = value
277
+ end
278
+
279
+ # @private
280
+ def strict_keyword_argument_matching?
281
+ @options[:strict_keyword_argument_matching]
282
+ end
283
+
284
+ class << self
285
+ # @private
286
+ def reset_configuration
287
+ @configuration = nil
288
+ end
289
+
290
+ # Temporarily modify {Configuration} options.
291
+ #
292
+ # The supplied +temporary_options+ will override the current configuration for the duration of the supplied block.
293
+ # The configuration will be returned to its original state when the block returns.
294
+ #
295
+ # @param [Hash] temporary_options the configuration options to apply for the duration of the block.
296
+ # @yield block during which the configuration change will be in force.
297
+ #
298
+ # @example Temporarily prevent stubbing of non-mock object
299
+ # Mocha::Configuration.override(stubbing_method_on_non_mock_object: :prevent) do
300
+ # 123.stubs(:to_s).returns('456')
301
+ # end
302
+ def override(temporary_options)
303
+ original_configuration = configuration
304
+ @configuration = configuration.merge(new(temporary_options))
305
+ yield
306
+ ensure
307
+ @configuration = original_configuration
308
+ end
309
+
310
+ # @private
311
+ def configuration
312
+ @configuration ||= new
313
+ end
314
+
315
+ private
316
+
317
+ # @private
318
+ def change_config(action, new_value, &block)
319
+ if block_given?
320
+ temporarily_change_config action, new_value, &block
321
+ else
322
+ configuration.send(:"#{action}=", new_value)
323
+ end
324
+ end
325
+
326
+ # @private
327
+ def temporarily_change_config(action, new_value)
328
+ original_configuration = configuration
329
+ new_configuration = configuration.dup
330
+ new_configuration.send(:"#{action}=", new_value)
331
+ @configuration = new_configuration
332
+ yield
333
+ ensure
334
+ @configuration = original_configuration
335
+ end
336
+ end
337
+ end
338
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mocha
4
+ class DefaultName
5
+ def initialize(mock)
6
+ @mock = mock
7
+ end
8
+
9
+ def mocha_inspect
10
+ address = @mock.__id__ * 2
11
+ address += 0x100000000 if address < 0
12
+ "#<Mock:0x#{format('%<address>x', address: address)}>"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mocha
4
+ class DefaultReceiver
5
+ def initialize(mock)
6
+ @mock = mock
7
+ end
8
+
9
+ def mocks
10
+ [@mock]
11
+ end
12
+ end
13
+ end
@@ -1,22 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mocha/backtrace_filter'
4
+
1
5
  module Mocha
2
-
3
6
  class Deprecation
4
-
7
+ class Logger
8
+ def call(message)
9
+ filter = BacktraceFilter.new
10
+ location = filter.filtered(caller)[0]
11
+ warn "Mocha deprecation warning at #{location}: #{message}"
12
+ end
13
+ end
14
+
5
15
  class << self
6
-
7
- attr_accessor :mode, :messages
8
-
16
+ attr_writer :logger
17
+
9
18
  def warning(message)
10
- @messages << message
11
- $stderr.puts "Mocha deprecation warning: #{message}" unless mode == :disabled
12
- $stderr.puts caller.join("\n ") if mode == :debug
19
+ logger.call(message)
13
20
  end
14
21
 
22
+ def logger
23
+ @logger ||= Logger.new
24
+ end
15
25
  end
16
-
17
- self.mode = :enabled
18
- self.messages = []
19
-
20
26
  end
21
-
22
- end
27
+ end