mocha 1.16.1 → 2.7.1

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +10 -13
  3. data/.yardopts +1 -2
  4. data/COPYING.md +2 -2
  5. data/Gemfile +7 -4
  6. data/MIT-LICENSE.md +1 -1
  7. data/README.md +23 -24
  8. data/RELEASE.md +294 -0
  9. data/Rakefile +23 -24
  10. data/lib/mocha/api.rb +30 -71
  11. data/lib/mocha/backtrace_filter.rb +2 -2
  12. data/lib/mocha/cardinality.rb +4 -0
  13. data/lib/mocha/configuration.rb +44 -126
  14. data/lib/mocha/debug.rb +2 -5
  15. data/lib/mocha/detection/{mini_test.rb → minitest.rb} +5 -5
  16. data/lib/mocha/detection/test_unit.rb +2 -2
  17. data/lib/mocha/expectation.rb +99 -12
  18. data/lib/mocha/expectation_error_factory.rb +2 -2
  19. data/lib/mocha/expectation_list.rb +8 -6
  20. data/lib/mocha/hooks.rb +10 -4
  21. data/lib/mocha/inspect.rb +15 -4
  22. data/lib/mocha/integration/{mini_test → minitest}/adapter.rb +21 -6
  23. data/lib/mocha/integration/{mini_test → minitest}/exception_translation.rb +2 -2
  24. data/lib/mocha/integration/minitest.rb +28 -0
  25. data/lib/mocha/integration/test_unit/adapter.rb +9 -4
  26. data/lib/mocha/integration/test_unit.rb +10 -31
  27. data/lib/mocha/invocation.rb +2 -15
  28. data/lib/mocha/minitest.rb +3 -6
  29. data/lib/mocha/mock.rb +45 -18
  30. data/lib/mocha/mockery.rb +13 -9
  31. data/lib/mocha/names.rb +1 -1
  32. data/lib/mocha/object_methods.rb +2 -2
  33. data/lib/mocha/parameter_matchers/base.rb +4 -9
  34. data/lib/mocha/parameter_matchers/equivalent_uri.rb +1 -2
  35. data/lib/mocha/parameter_matchers/has_entries.rb +7 -2
  36. data/lib/mocha/parameter_matchers/includes.rb +3 -3
  37. data/lib/mocha/parameter_matchers/instance_methods.rb +10 -2
  38. data/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb +66 -0
  39. data/lib/mocha/parameter_matchers/responds_with.rb +32 -5
  40. data/lib/mocha/parameters_matcher.rb +10 -6
  41. data/lib/mocha/ruby_version.rb +2 -9
  42. data/lib/mocha/stubbed_method.rb +3 -39
  43. data/lib/mocha/test_unit.rb +1 -4
  44. data/lib/mocha/version.rb +1 -1
  45. data/mocha.gemspec +11 -1
  46. metadata +31 -31
  47. data/init.rb +0 -1
  48. data/lib/mocha/integration/mini_test/nothing.rb +0 -19
  49. data/lib/mocha/integration/mini_test/version_13.rb +0 -54
  50. data/lib/mocha/integration/mini_test/version_140.rb +0 -54
  51. data/lib/mocha/integration/mini_test/version_141.rb +0 -65
  52. data/lib/mocha/integration/mini_test/version_142_to_172.rb +0 -65
  53. data/lib/mocha/integration/mini_test/version_200.rb +0 -66
  54. data/lib/mocha/integration/mini_test/version_201_to_222.rb +0 -66
  55. data/lib/mocha/integration/mini_test/version_2110_to_2111.rb +0 -70
  56. data/lib/mocha/integration/mini_test/version_2112_to_320.rb +0 -73
  57. data/lib/mocha/integration/mini_test/version_230_to_2101.rb +0 -68
  58. data/lib/mocha/integration/mini_test.rb +0 -56
  59. data/lib/mocha/integration/test_unit/gem_version_200.rb +0 -62
  60. data/lib/mocha/integration/test_unit/gem_version_201_to_202.rb +0 -62
  61. data/lib/mocha/integration/test_unit/gem_version_203_to_220.rb +0 -62
  62. data/lib/mocha/integration/test_unit/gem_version_230_to_250.rb +0 -68
  63. data/lib/mocha/integration/test_unit/nothing.rb +0 -19
  64. data/lib/mocha/integration/test_unit/ruby_version_186_and_above.rb +0 -63
  65. data/lib/mocha/integration.rb +0 -11
  66. data/lib/mocha/setup.rb +0 -14
  67. data/yard-templates/default/layout/html/google_analytics.erb +0 -8
  68. data/yard-templates/default/layout/html/setup.rb +0 -5
data/Rakefile CHANGED
@@ -5,9 +5,14 @@ end
5
5
  require 'bundler/setup'
6
6
 
7
7
  require 'rake/testtask'
8
+ begin
9
+ # Only available with default Gemfile and in Ruby >= v2.2
10
+ require 'rubocop/rake_task'
11
+ rescue LoadError # rubocop:disable Lint/HandleExceptions
12
+ end
8
13
 
9
- desc 'Run all tests'
10
- task 'default' => ['test', 'test:performance']
14
+ desc 'Run all linters and tests'
15
+ task 'default' => ['lint', 'test', 'test:performance']
11
16
 
12
17
  desc 'Run tests'
13
18
  task 'test' do
@@ -37,10 +42,10 @@ namespace 'test' do # rubocop:disable Metrics/BlockLength
37
42
  end
38
43
 
39
44
  namespace 'integration' do
40
- desc 'Run MiniTest integration tests (intended to be run in its own process)'
45
+ desc 'Run Minitest integration tests (intended to be run in its own process)'
41
46
  Rake::TestTask.new('minitest') do |t|
42
47
  t.libs << 'test'
43
- t.test_files = FileList['test/integration/mini_test_test.rb']
48
+ t.test_files = FileList['test/integration/minitest_test.rb']
44
49
  t.verbose = true
45
50
  t.warning = true
46
51
  end
@@ -76,37 +81,37 @@ namespace 'test' do # rubocop:disable Metrics/BlockLength
76
81
  end
77
82
  end
78
83
 
79
- begin
80
- require 'rubocop/rake_task'
81
- if RUBY_VERSION >= '2.2.0' && (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby') && ENV['MOCHA_RUN_INTEGRATION_TESTS'].nil?
84
+ task 'lint' do
85
+ if defined?(RuboCop::RakeTask)
82
86
  RuboCop::RakeTask.new
83
- task 'test' => 'rubocop'
87
+ Rake::Task['rubocop'].invoke
88
+ else
89
+ puts 'RuboCop not available - skipping linting'
84
90
  end
85
- rescue LoadError # rubocop:disable Lint/HandleExceptions
86
91
  end
87
92
 
88
93
  # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
89
94
  def benchmark_test_case(klass, iterations)
90
95
  require 'benchmark'
91
- require 'mocha/detection/mini_test'
96
+ require 'mocha/detection/minitest'
92
97
 
93
- if defined?(MiniTest)
94
- minitest_version = Gem::Version.new(Mocha::Detection::MiniTest.version)
98
+ if defined?(Minitest)
99
+ minitest_version = Gem::Version.new(Mocha::Detection::Minitest.version)
95
100
  if Gem::Requirement.new('>= 5.0.0').satisfied_by?(minitest_version)
96
101
  Minitest.seed = 1
97
- result = Benchmark.realtime { iterations.times { |_i| klass.run(MiniTest::CompositeReporter.new) } }
98
- MiniTest::Runnable.runnables.delete(klass)
102
+ result = Benchmark.realtime { iterations.times { |_i| klass.run(Minitest::CompositeReporter.new) } }
103
+ Minitest::Runnable.runnables.delete(klass)
99
104
  result
100
105
  else
101
- MiniTest::Unit.output = StringIO.new
102
- Benchmark.realtime { iterations.times { |_i| MiniTest::Unit.new.run([klass]) } }
106
+ Minitest::Unit.output = StringIO.new
107
+ Benchmark.realtime { iterations.times { |_i| Minitest::Unit.new.run([klass]) } }
103
108
  end
104
109
  else
105
110
  load 'test/unit/ui/console/testrunner.rb' unless defined?(Test::Unit::UI::Console::TestRunner)
106
111
  unless @silent_option
107
112
  begin
108
113
  load 'test/unit/ui/console/outputlevel.rb' unless defined?(Test::Unit::UI::Console::OutputLevel::SILENT)
109
- @silent_option = { :output_level => Test::Unit::UI::Console::OutputLevel::SILENT }
114
+ @silent_option = { output_level: Test::Unit::UI::Console::OutputLevel::SILENT }
110
115
  rescue LoadError
111
116
  @silent_option = Test::Unit::UI::SILENT
112
117
  end
@@ -124,14 +129,8 @@ if ENV['MOCHA_GENERATE_DOCS']
124
129
  `rm -rf ./docs`
125
130
  end
126
131
 
127
- task 'docs_environment' do
128
- unless ENV['GOOGLE_ANALYTICS_WEB_PROPERTY_ID']
129
- puts "\nWarning: GOOGLE_ANALYTICS_WEB_PROPERTY_ID was not defined\n\n"
130
- end
131
- end
132
-
133
132
  desc 'Generate documentation'
134
- YARD::Rake::YardocTask.new('yardoc' => 'docs_environment') do |task|
133
+ YARD::Rake::YardocTask.new('yardoc') do |task|
135
134
  task.options = ['--title', "Mocha #{Mocha::VERSION}", '--fail-on-warning']
136
135
  end
137
136
 
data/lib/mocha/api.rb CHANGED
@@ -7,7 +7,7 @@ require 'mocha/object_methods'
7
7
  require 'mocha/class_methods'
8
8
 
9
9
  module Mocha
10
- # Methods added to +Test::Unit::TestCase+, +MiniTest::Unit::TestCase+ or equivalent.
10
+ # Methods added to +Test::Unit::TestCase+, +Minitest::Unit::TestCase+ or equivalent.
11
11
  # The mock creation methods are {#mock}, {#stub} and {#stub_everything}, all of which return a #{Mock}
12
12
  # which can be further modified by {Mock#responds_like} and {Mock#responds_like_instance_of} methods,
13
13
  # both of which return a {Mock}, too, and can therefore, be chained to the original creation methods.
@@ -52,7 +52,6 @@ module Mocha
52
52
  #
53
53
  # @overload def mock(name)
54
54
  # @param [String, Symbol] name identifies mock object in error messages.
55
- # @note Prior to v1.10.0 when +name+ was a +Symbol+, this method returned an unnamed +Mock+ that expected the method identified by +name+. This was undocumented behaviour and it will be removed in the future, but for the moment it can be reinstated using {Configuration#reinstate_undocumented_behaviour_from_v1_9=}.
56
55
  # @overload def mock(expected_methods_vs_return_values = {})
57
56
  # @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 {Mock#expects} were called multiple times.
58
57
  # @overload def mock(name, expected_methods_vs_return_values = {})
@@ -61,32 +60,14 @@ module Mocha
61
60
  #
62
61
  # @example Using expected_methods_vs_return_values Hash to setup expectations.
63
62
  # def test_motor_starts_and_stops
64
- # motor = mock('motor', :start => true, :stop => true)
63
+ # motor = mock('motor', start: true, stop: true)
65
64
  # assert motor.start
66
65
  # assert motor.stop
67
66
  # # an error will be raised unless both Motor#start and Motor#stop have been called
68
67
  # end
69
68
  #
70
- def mock(*arguments) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
71
- if Mocha.configuration.reinstate_undocumented_behaviour_from_v1_9?
72
- if arguments.first.is_a?(Symbol)
73
- method_name = arguments[0]
74
- Deprecation.warning(
75
- "Explicitly include `#{method_name}` in Hash of expected methods vs return values,",
76
- " e.g. `mock(:#{method_name} => nil)`."
77
- )
78
- if arguments[1]
79
- Deprecation.warning(
80
- "In this case the 2nd argument for `mock(:##{method_name}, ...)` is ignored,",
81
- ' but in the future a Hash of expected methods vs return values will be respected.'
82
- )
83
- end
84
- elsif arguments.first.is_a?(String)
85
- name = arguments.shift
86
- end
87
- elsif arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
88
- name = arguments.shift
89
- end
69
+ def mock(*arguments)
70
+ name = arguments.shift.to_s if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
90
71
  expectations = arguments.shift || {}
91
72
  mock = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
92
73
  mock.expects(expectations)
@@ -99,46 +80,26 @@ module Mocha
99
80
  #
100
81
  # @overload def stub(name)
101
82
  # @param [String, Symbol] name identifies mock object in error messages.
102
- # @note Prior to v1.10.0 when +name+ was a +Symbol+, this method returned an unnamed +Mock+ that stubbed the method identified by +name+. This was undocumented behaviour and it will be removed in the future, but for the moment it can be reinstated using {Configuration#reinstate_undocumented_behaviour_from_v1_9=}.
103
83
  # @overload def stub(stubbed_methods_vs_return_values = {})
104
84
  # @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 {Mock#stubs} were called multiple times.
105
85
  # @overload def stub(name, stubbed_methods_vs_return_values = {})
106
86
  # @param [String, Symbol] name identifies mock object in error messages.
87
+ # @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 {Mock#stubs} were called multiple times.
107
88
  #
108
89
  # @example Using stubbed_methods_vs_return_values Hash to setup stubbed methods.
109
90
  # def test_motor_starts_and_stops
110
- # motor = stub('motor', :start => true, :stop => true)
91
+ # motor = stub('motor', start: true, stop: true)
111
92
  # assert motor.start
112
93
  # assert motor.stop
113
94
  # # an error will not be raised even if either Motor#start or Motor#stop has not been called
114
95
  # end
115
- # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
116
96
  def stub(*arguments)
117
- if Mocha.configuration.reinstate_undocumented_behaviour_from_v1_9?
118
- if arguments.first.is_a?(Symbol)
119
- method_name = arguments[0]
120
- Deprecation.warning(
121
- "Explicitly include `#{method_name}` in Hash of stubbed methods vs return values,",
122
- " e.g. `stub(:#{method_name} => nil)`."
123
- )
124
- if arguments[1]
125
- Deprecation.warning(
126
- "In this case the 2nd argument for `stub(:##{method_name}, ...)` is ignored,",
127
- ' but in the future a Hash of stubbed methods vs return values will be respected.'
128
- )
129
- end
130
- elsif arguments.first.is_a?(String)
131
- name = arguments.shift
132
- end
133
- elsif arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
134
- name = arguments.shift
135
- end
97
+ name = arguments.shift.to_s if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
136
98
  expectations = arguments.shift || {}
137
99
  stub = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
138
100
  stub.stubs(expectations)
139
101
  stub
140
102
  end
141
- # rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
142
103
 
143
104
  # Builds a mock object that accepts calls to any method. By default it will return +nil+ for any method call.
144
105
  #
@@ -146,7 +107,6 @@ module Mocha
146
107
  #
147
108
  # @overload def stub_everything(name)
148
109
  # @param [String, Symbol] name identifies mock object in error messages.
149
- # @note Prior to v1.10.0 when +name+ was a +Symbol+, this method returned an unnamed +Mock+ that stubbed the method identified by +name+. This was undocumented behaviour and it will be removed in the future, but for the moment it can be reinstated using {Configuration#reinstate_undocumented_behaviour_from_v1_9=}.
150
110
  # @overload def stub_everything(stubbed_methods_vs_return_values = {})
151
111
  # @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 {Mock#stubs} were called multiple times.
152
112
  # @overload def stub_everything(name, stubbed_methods_vs_return_values = {})
@@ -155,44 +115,27 @@ module Mocha
155
115
  #
156
116
  # @example Ignore invocations of irrelevant methods.
157
117
  # def test_motor_stops
158
- # motor = stub_everything('motor', :stop => true)
118
+ # motor = stub_everything('motor', stop: true)
159
119
  # assert_nil motor.irrelevant_method_1 # => no error raised
160
120
  # assert_nil motor.irrelevant_method_2 # => no error raised
161
121
  # assert motor.stop
162
122
  # end
163
- # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
164
123
  def stub_everything(*arguments)
165
- if Mocha.configuration.reinstate_undocumented_behaviour_from_v1_9?
166
- if arguments.first.is_a?(Symbol)
167
- method_name = arguments[0]
168
- Deprecation.warning(
169
- "Explicitly include `#{method_name}` in Hash of stubbed methods vs return values,",
170
- " e.g. `stub_everything(:#{method_name} => nil)`."
171
- )
172
- if arguments[1]
173
- Deprecation.warning(
174
- "In this case the 2nd argument for `stub_everything(:##{method_name}, ...)` is ignored,",
175
- ' but in the future a Hash of stubbed methods vs return values will be respected.'
176
- )
177
- end
178
- elsif arguments.first.is_a?(String)
179
- name = arguments.shift
180
- end
181
- elsif arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
182
- name = arguments.shift
183
- end
124
+ name = arguments.shift if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
184
125
  expectations = arguments.shift || {}
185
126
  stub = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
186
127
  stub.stub_everything
187
128
  stub.stubs(expectations)
188
129
  stub
189
130
  end
190
- # rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
191
131
 
192
132
  # Builds a new sequence which can be used to constrain the order in which expectations can occur.
193
133
  #
194
- # Specify that an expected invocation must occur within a named {Sequence} by using {Expectation#in_sequence}.
134
+ # Specify that an expected invocation must occur within a named {Sequence} by calling {Expectation#in_sequence}
135
+ # on each expectation or by passing a block within which all expectations should be constrained by the {Sequence}.
195
136
  #
137
+ # @param [String] name name of sequence
138
+ # @yield optional block within which expectations should be constrained by the sequence
196
139
  # @return [Sequence] a new sequence
197
140
  #
198
141
  # @see Expectation#in_sequence
@@ -216,8 +159,23 @@ module Mocha
216
159
  #
217
160
  # task_one.execute
218
161
  # task_two.execute
162
+ #
163
+ # @example Ensure methods on egg are invoked in the correct order using a block.
164
+ # egg = mock('egg')
165
+ # sequence('breakfast') do
166
+ # egg.expects(:crack)
167
+ # egg.expects(:fry)
168
+ # egg.expects(:eat)
169
+ # end
219
170
  def sequence(name)
220
- Sequence.new(name)
171
+ Sequence.new(name).tap do |seq|
172
+ Mockery.instance.sequences.push(seq)
173
+ begin
174
+ yield if block_given?
175
+ ensure
176
+ Mockery.instance.sequences.pop
177
+ end
178
+ end
221
179
  end
222
180
 
223
181
  # Builds a new state machine which can be used to constrain the order in which expectations can occur.
@@ -230,6 +188,7 @@ module Mocha
230
188
  #
231
189
  # A test can contain multiple state machines.
232
190
  #
191
+ # @param [String] name name of state machine
233
192
  # @return [StateMachine] a new state machine
234
193
  #
235
194
  # @see Expectation#then
@@ -3,11 +3,11 @@ module Mocha
3
3
  LIB_DIRECTORY = File.expand_path(File.join(File.dirname(__FILE__), '..')) + File::SEPARATOR
4
4
 
5
5
  def initialize(lib_directory = LIB_DIRECTORY)
6
- @path_pattern = Regexp.new(lib_directory)
6
+ @lib_directory = lib_directory
7
7
  end
8
8
 
9
9
  def filtered(backtrace)
10
- backtrace.reject { |location| @path_pattern.match(File.expand_path(location)) }
10
+ backtrace.reject { |location| File.expand_path(location).start_with?(@lib_directory) }
11
11
  end
12
12
  end
13
13
  end
@@ -34,6 +34,10 @@ module Mocha
34
34
  @invocations.size < maximum
35
35
  end
36
36
 
37
+ def invocations_never_allowed?
38
+ maximum.zero?
39
+ end
40
+
37
41
  def satisfied?
38
42
  @invocations.size >= required
39
43
  end
@@ -1,3 +1,6 @@
1
+ require 'mocha/ruby_version'
2
+ require 'mocha/deprecation'
3
+
1
4
  module Mocha
2
5
  # Allows setting of configuration options. See {Configuration} for the available options.
3
6
  #
@@ -37,13 +40,13 @@ module Mocha
37
40
  class Configuration
38
41
  # @private
39
42
  DEFAULTS = {
40
- :stubbing_method_unnecessarily => :allow,
41
- :stubbing_method_on_non_mock_object => :allow,
42
- :stubbing_non_existent_method => :allow,
43
- :stubbing_non_public_method => :allow,
44
- :stubbing_method_on_nil => :prevent,
45
- :display_matching_invocations_on_failure => false,
46
- :reinstate_undocumented_behaviour_from_v1_9 => false
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
+ stubbing_method_on_nil: :prevent,
48
+ display_matching_invocations_on_failure: false,
49
+ strict_keyword_argument_matching: false
47
50
  }.freeze
48
51
 
49
52
  attr_reader :options
@@ -204,8 +207,14 @@ module Mocha
204
207
  # When +value+ is +:prevent+, raise a {StubbingError}. This is the default.
205
208
  #
206
209
  # @param [Symbol] value one of +:allow+, +:warn+, +:prevent+.
210
+ # @deprecated This method is deprecated and will be removed in a future release. +nil+ is frozen in Ruby >= v2.2 and Mocha will be dropping support for Ruby v2.1. At that point it won't be possible to stub methods on +nil+ any more.
207
211
  #
208
212
  def stubbing_method_on_nil=(value)
213
+ Deprecation.warning([
214
+ '`Mocha::Configuration#stubbing_method_on_nil=` is deprecated and will be removed in a future release.',
215
+ '`nil` is frozen in Ruby >= v2.2 and Mocha will be dropping support for Ruby v2.1.',
216
+ "At that point it won't be possible to stub methods on `nil` any more."
217
+ ].join(' '))
209
218
  @options[:stubbing_method_on_nil] = value
210
219
  end
211
220
 
@@ -248,145 +257,54 @@ module Mocha
248
257
  @options[:display_matching_invocations_on_failure]
249
258
  end
250
259
 
251
- # Reinstate undocumented behaviour from v1.9
252
- #
253
- # Previously when {API#mock}, {API#stub}, or {API#stub_everything} were called with the first argument being a symbol, they built an *unnamed* mock object *and* expected or stubbed the method identified by the symbol argument; subsequent arguments were ignored.
254
- # Now these methods build a *named* mock with the name specified by the symbol argument; *no* methods are expected or stubbed and subsequent arguments *are* taken into account.
260
+ # Perform strict keyword argument comparison. Only supported in Ruby >= v2.7.
255
261
  #
256
- # Previously if {Expectation#yields} or {Expectation#multiple_yields} was called on an expectation, but no block was given when the method was invoked, the instruction to yield was ignored.
257
- # Now a +LocalJumpError+ is raised.
262
+ # 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. This is because {#strict_keyword_argument_matching=} will default to +true+ in the future.
258
263
  #
259
- # Enabling this configuration option reinstates the previous behaviour, but displays a deprecation warning.
264
+ # 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}.
260
265
  #
261
- # @param [Boolean] value +true+ to reinstate undocumented behaviour; disabled by default.
262
- # @deprecated Fix deprecation warnings caused by reliance on v1.9 behaviour and remove calls to this method.
266
+ # Note that +Hash+-related matchers such as {ParameterMatchers#has_value} or {ParameterMatchers#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.
263
267
  #
264
- # @example Reinstate undocumented behaviour for {API#mock}
265
- # Mocha.configure do |c|
266
- # c.reinstate_undocumented_behaviour_from_v1_9 = true
267
- # end
268
+ # This configuration option is +false+ by default to enable gradual adoption, but will be +true+ by default in the future.
268
269
  #
269
- # foo = mock(:bar)
270
- # foo.inspect # => #<Mock>
270
+ # @param [Boolean] value +true+ to enable strict keyword argument matching; +false+ by default.
271
271
  #
272
- # not all expectations were satisfied
273
- # unsatisfied expectations:
274
- # - expected exactly once, invoked never: #<Mock>.foo
272
+ # @example Loose keyword argument matching (default)
275
273
  #
276
- # @example Reinstate undocumented behaviour for {API#stub}
277
- # Mocha.configure do |c|
278
- # c.reinstate_undocumented_behaviour_from_v1_9 = true
274
+ # class Example
275
+ # def foo(a, bar:); end
279
276
  # end
280
277
  #
281
- # foo = stub(:bar)
282
- # foo.inspect # => #<Mock>
283
- # foo.bar # => nil
278
+ # example = Example.new
279
+ # example.expects(:foo).with('a', bar: 'b')
280
+ # example.foo('a', { bar: 'b' })
281
+ # # This passes the test, but would result in an ArgumentError in practice
284
282
  #
285
- # @example Reinstate undocumented behaviour for {Expectation#yields}
286
- # foo = mock('foo')
287
- # foo.stubs(:my_method).yields(1, 2)
288
- # foo.my_method # => raises LocalJumpError when no block is supplied
283
+ # @example Strict keyword argument matching
289
284
  #
290
285
  # Mocha.configure do |c|
291
- # c.reinstate_undocumented_behaviour_from_v1_9 = true
286
+ # c.strict_keyword_argument_matching = true
292
287
  # end
293
288
  #
294
- # foo = mock('foo')
295
- # foo.stubs(:my_method).yields(1, 2)
296
- # foo.my_method # => does *not* raise LocalJumpError when no block is supplied
297
- #
298
- def reinstate_undocumented_behaviour_from_v1_9=(value)
299
- if value
300
- sentence1 = 'Configuration#reinstate_undocumented_behaviour_from_v1_9= will be removed in the future.'
301
- sentence2 = 'Fix deprecation warnings caused by reliance on v1.9 behaviour.'
302
- sentence3 = 'See docs for API#mock, API#stub, API#stub_everything, Expectation#yields and Expectation#multiple_yields.'
303
- Deprecation.warning([sentence1, sentence2, sentence3].join(' '))
304
- else
305
- sentence1 = 'Configuration#reinstate_undocumented_behaviour_from_v1_9= is unnecessarily being set to false, because this is now the default value.'
306
- sentence2 = 'Configuration#reinstate_undocumented_behaviour_from_v1_9= will be removed in the future, so you should avoid calling it.'
307
- Deprecation.warning([sentence1, sentence2].join(' '))
308
- end
309
- @options[:reinstate_undocumented_behaviour_from_v1_9] = value
289
+ # class Example
290
+ # def foo(a, bar:); end
291
+ # end
292
+ #
293
+ # example = Example.new
294
+ # example.expects(:foo).with('a', bar: 'b')
295
+ # example.foo('a', { bar: 'b' })
296
+ # # This now fails as expected
297
+ def strict_keyword_argument_matching=(value)
298
+ raise 'Strict keyword argument matching requires Ruby 2.7 and above.' unless Mocha::RUBY_V27_PLUS
299
+ @options[:strict_keyword_argument_matching] = value
310
300
  end
311
301
 
312
302
  # @private
313
- def reinstate_undocumented_behaviour_from_v1_9?
314
- @options[:reinstate_undocumented_behaviour_from_v1_9]
303
+ def strict_keyword_argument_matching?
304
+ @options[:strict_keyword_argument_matching]
315
305
  end
316
306
 
317
307
  class << self
318
- # Allow the specified +action+.
319
- #
320
- # @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
321
- # @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
322
- # @deprecated If a block is supplied, call {.override} with a +Hash+ containing an entry with the +action+ as the key and +:allow+ as the value. If no block is supplied, call the appropriate +action+ writer method with +value+ set to +:allow+ via {Mocha.configure}. The writer method will be the one of the following corresponding to the +action+:
323
- # * {#stubbing_method_unnecessarily=}
324
- # * {#stubbing_method_on_non_mock_object=}
325
- # * {#stubbing_non_existent_method=}
326
- # * {#stubbing_non_public_method=}
327
- # * {#stubbing_method_on_nil=}
328
- def allow(action, &block)
329
- if block_given?
330
- Deprecation.warning("Use Mocha::Configuration.override(#{action}: :allow) with the same block")
331
- else
332
- Deprecation.warning("Use Mocha.configure { |c| c.#{action} = :allow }")
333
- end
334
- change_config action, :allow, &block
335
- end
336
-
337
- # @private
338
- def allow?(action)
339
- configuration.allow?(action)
340
- end
341
-
342
- # Warn if the specified +action+ is attempted.
343
- #
344
- # @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
345
- # @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
346
- # @deprecated If a block is supplied, call {.override} with a +Hash+ containing an entry with the +action+ as the key and +:warn+ as the value. If no block is supplied, call the appropriate +action+ writer method with +value+ set to +:warn+ via {Mocha.configure}. The writer method will be the one of the following corresponding to the +action+:
347
- # * {#stubbing_method_unnecessarily=}
348
- # * {#stubbing_method_on_non_mock_object=}
349
- # * {#stubbing_non_existent_method=}
350
- # * {#stubbing_non_public_method=}
351
- # * {#stubbing_method_on_nil=}
352
- def warn_when(action, &block)
353
- if block_given?
354
- Deprecation.warning("Use Mocha::Configuration.override(#{action}: :warn) with the same block")
355
- else
356
- Deprecation.warning("Use Mocha.configure { |c| c.#{action} = :warn }")
357
- end
358
- change_config action, :warn, &block
359
- end
360
-
361
- # @private
362
- def warn_when?(action)
363
- configuration.warn_when?(action)
364
- end
365
-
366
- # Raise a {StubbingError} if the specified +action+ is attempted.
367
- #
368
- # @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
369
- # @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
370
- # @deprecated If a block is supplied, call {.override} with a +Hash+ containing an entry with the +action+ as the key and +:prevent+ as the value. If no block is supplied, call the appropriate +action+ writer method with +value+ set to +:prevent+ via {Mocha.configure}. The writer method will be the one of the following corresponding to the +action+:
371
- # * {#stubbing_method_unnecessarily=}
372
- # * {#stubbing_method_on_non_mock_object=}
373
- # * {#stubbing_non_existent_method=}
374
- # * {#stubbing_non_public_method=}
375
- # * {#stubbing_method_on_nil=}
376
- def prevent(action, &block)
377
- if block_given?
378
- Deprecation.warning("Use Mocha::Configuration.override(#{action}: :prevent) with the same block")
379
- else
380
- Deprecation.warning("Use Mocha.configure { |c| c.#{action} = :prevent }")
381
- end
382
- change_config action, :prevent, &block
383
- end
384
-
385
- # @private
386
- def prevent?(action)
387
- configuration.prevent?(action)
388
- end
389
-
390
308
  # @private
391
309
  def reset_configuration
392
310
  @configuration = nil
data/lib/mocha/debug.rb CHANGED
@@ -1,12 +1,9 @@
1
1
  module Mocha
2
2
  module Debug
3
- OPTIONS = (ENV['MOCHA_OPTIONS'] || '').split(',').inject({}) do |hash, key|
4
- hash[key] = true
5
- hash
6
- end.freeze
3
+ OPTIONS = (ENV['MOCHA_OPTIONS'] || '').split(',').freeze
7
4
 
8
5
  def self.puts(message)
9
- warn(message) if OPTIONS['debug']
6
+ warn(message) if OPTIONS.include?('debug')
10
7
  end
11
8
  end
12
9
  end
@@ -1,17 +1,17 @@
1
1
  module Mocha
2
2
  module Detection
3
- module MiniTest
3
+ module Minitest
4
4
  def self.testcase
5
5
  if defined?(::Minitest::Test)
6
6
  ::Minitest::Test
7
- elsif defined?(::MiniTest::Unit::TestCase)
8
- ::MiniTest::Unit::TestCase
7
+ elsif defined?(::Minitest::Unit::TestCase)
8
+ ::Minitest::Unit::TestCase
9
9
  end
10
10
  end
11
11
 
12
12
  def self.version
13
- if defined?(::MiniTest::Unit::VERSION)
14
- ::MiniTest::Unit::VERSION
13
+ if defined?(::Minitest::Unit::VERSION)
14
+ ::Minitest::Unit::VERSION
15
15
  elsif defined?(::Minitest::VERSION)
16
16
  ::Minitest::VERSION
17
17
  else
@@ -3,8 +3,8 @@ module Mocha
3
3
  module TestUnit
4
4
  def self.testcase
5
5
  if defined?(::Test::Unit::TestCase) &&
6
- !(defined?(::MiniTest::Unit::TestCase) && (::Test::Unit::TestCase < ::MiniTest::Unit::TestCase)) &&
7
- !(defined?(::MiniTest::Spec) && (::Test::Unit::TestCase < ::MiniTest::Spec))
6
+ !(defined?(::Minitest::Unit::TestCase) && (::Test::Unit::TestCase < ::Minitest::Unit::TestCase)) &&
7
+ !(defined?(::Minitest::Spec) && (::Test::Unit::TestCase < ::Minitest::Spec))
8
8
  ::Test::Unit::TestCase
9
9
  end
10
10
  end