mocha 1.15.0 → 2.0.0.alpha.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -1
  3. data/Gemfile +3 -3
  4. data/RELEASE.md +42 -0
  5. data/Rakefile +12 -7
  6. data/lib/mocha/api.rb +5 -65
  7. data/lib/mocha/configuration.rb +30 -108
  8. data/lib/mocha/expectation.rb +62 -7
  9. data/lib/mocha/inspect.rb +2 -2
  10. data/lib/mocha/integration/mini_test.rb +10 -38
  11. data/lib/mocha/integration/test_unit/adapter.rb +1 -1
  12. data/lib/mocha/integration/test_unit.rb +10 -31
  13. data/lib/mocha/invocation.rb +2 -15
  14. data/lib/mocha/minitest.rb +1 -4
  15. data/lib/mocha/mock.rb +13 -9
  16. data/lib/mocha/parameter_matchers/base.rb +1 -1
  17. data/lib/mocha/parameter_matchers/equivalent_uri.rb +0 -1
  18. data/lib/mocha/parameter_matchers/instance_methods.rb +10 -1
  19. data/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb +64 -0
  20. data/lib/mocha/parameters_matcher.rb +3 -3
  21. data/lib/mocha/ruby_version.rb +1 -9
  22. data/lib/mocha/stubbed_method.rb +3 -39
  23. data/lib/mocha/test_unit.rb +1 -4
  24. data/lib/mocha/version.rb +1 -1
  25. data/mocha.gemspec +1 -1
  26. metadata +10 -28
  27. data/init.rb +0 -1
  28. data/lib/mocha/integration/mini_test/nothing.rb +0 -19
  29. data/lib/mocha/integration/mini_test/version_13.rb +0 -54
  30. data/lib/mocha/integration/mini_test/version_140.rb +0 -54
  31. data/lib/mocha/integration/mini_test/version_141.rb +0 -65
  32. data/lib/mocha/integration/mini_test/version_142_to_172.rb +0 -65
  33. data/lib/mocha/integration/mini_test/version_200.rb +0 -66
  34. data/lib/mocha/integration/mini_test/version_201_to_222.rb +0 -66
  35. data/lib/mocha/integration/mini_test/version_2110_to_2111.rb +0 -70
  36. data/lib/mocha/integration/mini_test/version_2112_to_320.rb +0 -73
  37. data/lib/mocha/integration/mini_test/version_230_to_2101.rb +0 -68
  38. data/lib/mocha/integration/test_unit/gem_version_200.rb +0 -62
  39. data/lib/mocha/integration/test_unit/gem_version_201_to_202.rb +0 -62
  40. data/lib/mocha/integration/test_unit/gem_version_203_to_220.rb +0 -62
  41. data/lib/mocha/integration/test_unit/gem_version_230_to_250.rb +0 -68
  42. data/lib/mocha/integration/test_unit/nothing.rb +0 -19
  43. data/lib/mocha/integration/test_unit/ruby_version_186_and_above.rb +0 -63
  44. data/lib/mocha/integration.rb +0 -11
  45. data/lib/mocha/setup.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6be1c2dafee7237e2ff675159cbc86afd1b5a56493d2671eba64cc4fbb001dd
4
- data.tar.gz: 2bde2824fd9b2f5a3ba6445f9f3e42e1e97053f235eb5fea647410f037a0cee2
3
+ metadata.gz: 97e7753fb6b5d10c44315d7b40ab4004dd1ee6224af909d983e2553d6541701b
4
+ data.tar.gz: 917eff26dfcbdfa01dc4c1befcb43556075d87b0b9929ac055498e2a9ec4e622
5
5
  SHA512:
6
- metadata.gz: fd3c082fca1dd5123b1c7ac6dd65ba4c9a2e92ec8791dcbc6f3d4a9a9f9c8161cf8cf4b9058c7483f5419118cdfea6cfc17cc0b206964dfc98ed8271852ea5ee
7
- data.tar.gz: 074fcc5048dd385eb929c03b5757c517eaceb0f72e8536500d867b3571f072a7aa29afc8a7a78a4ab0db3c7208a5c811c0a5754d30b9ebaec213ea1337f17eea
6
+ metadata.gz: 9421147bdeb37a99308e577e284248f0c2ddad246772d4822a0d97c11ed4e393e12f5157be6832d0bf7ab58b60227910282b5ec315615476b27ecc7ff67523fc
7
+ data.tar.gz: 1a63c4651b41de00f65ed771f1b22d0e6b63c1d795b9c36b195889fac3bd55168399d0b3a7cc6dfae77fcfdbfffce8098e8f5b6a8cef4fc49f481a533d844c6d
data/.rubocop.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  inherit_from: .rubocop_todo.yml
2
2
 
3
3
  AllCops:
4
- TargetRubyVersion: 2.2 # closest to required_ruby_version of '>= 1.9'
4
+ TargetRubyVersion: 2.2 # closest to required_ruby_version of '>= 2.0'
5
5
 
6
6
  # Even the reference in the documentation suggests that you should prefer
7
7
  # `alias_method` vs `alias`, so I don't understand why that isn't the default.
@@ -48,6 +48,10 @@ Style/WhileUntilModifier:
48
48
  Style/AccessModifierDeclarations:
49
49
  Enabled: false
50
50
 
51
+ # This is useful when using `ExecutionPoint.current` to make tests more robust
52
+ Style/Semicolon:
53
+ Enabled: false
54
+
51
55
  # Enabling this cop results in an "Infinite loop detected" exception
52
56
  Layout/AccessModifierIndentation:
53
57
  Enabled: false
data/Gemfile CHANGED
@@ -3,9 +3,7 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  # rubocop:disable Bundler/DuplicatedGem
6
- if RUBY_VERSION < '2'
7
- gem 'rake', '~> 12.2.1'
8
- elsif RUBY_VERSION < '2.2'
6
+ if RUBY_VERSION < '2.2'
9
7
  gem 'rake', '~> 12.3.3'
10
8
  else
11
9
  gem 'rake'
@@ -30,3 +28,5 @@ if ENV['MOCHA_GENERATE_DOCS']
30
28
  gem 'redcarpet'
31
29
  gem 'yard'
32
30
  end
31
+
32
+ gem 'ruby2_keywords', '~> 0.0.5'
data/RELEASE.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # Release Notes
2
2
 
3
+ ## 2.0.0.alpha.1
4
+
5
+ ### External changes
6
+
7
+ * Improve strict keyword argument matching deprecation warning by including the source location of the stub definition (77c0d4cc)
8
+
9
+ ### Internal changes
10
+
11
+ * Disable Style/Semicolon cop globally (8cd0b705)
12
+
13
+ ## 2.0.0.alpha
14
+
15
+ ### External changes
16
+
17
+ * Remove support for Ruby v1.9 - thanks to @wasabigeek (#552)
18
+ * Support strict keyword argument matching - see docs for `Expectation#with` & `Configuration#strict_keyword_argument_matching=` - thanks to @wasabigeek (#446,#535,#544,#562)
19
+ * Deprecate `Hash` args that don't strictly match (#563,981c31be)
20
+ * Drop support for older versions of test-unit - gem versions of test-unit earlier than v2.5.1 and versions of test-unit
21
+ from the Ruby v1.8 standard library are no longer supported (#540,969f4845)
22
+ * Drop support for older versions of minitest - versions of minitest earlier than v3.3.0 are no longer supported (#541,ca69dc9e)
23
+ * Remove deprecated `mocha/setup.rb` mechanism (642a0ff4)
24
+ * Add missing docs for `API#stub` parameter (257b4cb4)
25
+ * Remove optional reinstatement of v1.9 behaviour (#436,#438,#569,1473ee25)
26
+ * Remove deprecated methods in `Configuration` (#421,e7ff7528)
27
+ * Fail fast when mock receives invocations in another test (#440,#442,cb054d59)
28
+ * Improve docs re using matchers in `Expectation#with` (da7237cd)
29
+ * Expand `Expectation#with` docs re keyword arguments (fed6808d)
30
+ * Improve docs for `strict_keyword_argument_matching` (8d8f881d)
31
+ * Remove deprecated Rails plugin `init.rb` file (1c617175)
32
+
33
+ ### Internal changes
34
+
35
+ * Separate linting from tests in terms of Rake tasks & CircleCI jobs - thanks to @wasabigeek (#556)
36
+ * Remove tests specific to Ruby v1.8 behaviour (46fca7ac, 3b369e99)
37
+ * Multi-line rubocop disable in `Mock#method_missing` (af2194c4)
38
+ * Remove unused arg for `HashMethods#mocha_inspect` (4f59e27f)
39
+ * Improve test runner assertions - failure vs error (eec7200a)
40
+ * Improve test coverage of `PositionalOrKeywordHash` (c294fe70)
41
+ * More consistent Test::Unit & Minitest integration (27dd3817)
42
+ * Remove redundant `require` statements (d82218a8,fa17b114)
43
+ * Add missing `require` statement (73493761)
44
+
3
45
  ## 1.15.0
4
46
 
5
47
  ### External changes
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
@@ -76,13 +81,13 @@ 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
data/lib/mocha/api.rb CHANGED
@@ -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 = {})
@@ -67,26 +66,8 @@ module Mocha
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,11 +80,11 @@ 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
@@ -112,33 +93,13 @@ module Mocha
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 = {})
@@ -160,34 +120,14 @@ module Mocha
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
  #
@@ -1,3 +1,5 @@
1
+ require 'mocha/ruby_version'
2
+
1
3
  module Mocha
2
4
  # Allows setting of configuration options. See {Configuration} for the available options.
3
5
  #
@@ -43,7 +45,7 @@ module Mocha
43
45
  :stubbing_non_public_method => :allow,
44
46
  :stubbing_method_on_nil => :prevent,
45
47
  :display_matching_invocations_on_failure => false,
46
- :reinstate_undocumented_behaviour_from_v1_9 => true
48
+ :strict_keyword_argument_matching => false
47
49
  }.freeze
48
50
 
49
51
  attr_reader :options
@@ -248,134 +250,54 @@ module Mocha
248
250
  @options[:display_matching_invocations_on_failure]
249
251
  end
250
252
 
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.
253
+ # Perform strict keyword argument comparison. Only supported in Ruby >= v2.7.
255
254
  #
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.
255
+ # 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 false negatives 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
256
  #
259
- # Enabling this configuration option reinstates the previous behaviour, but displays a deprecation warning.
257
+ # 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
258
  #
261
- # @param [Boolean] value +true+ to reinstate undocumented behaviour; enabled by default.
259
+ # 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 false negatives are still possible when they are used.
262
260
  #
263
- # @example Reinstate undocumented behaviour for {API#mock}
264
- # Mocha.configure do |c|
265
- # c.reinstate_undocumented_behaviour_from_v1_9 = true
266
- # end
261
+ # This configuration option is +false+ by default to enable gradual adoption, but will be +true+ by default in the future.
267
262
  #
268
- # foo = mock(:bar)
269
- # foo.inspect # => #<Mock>
263
+ # @param [Boolean] value +true+ to enable strict keyword argument matching; +false+ by default.
270
264
  #
271
- # not all expectations were satisfied
272
- # unsatisfied expectations:
273
- # - expected exactly once, invoked never: #<Mock>.foo
265
+ # @example Loose keyword argument matching (default)
274
266
  #
275
- # @example Reinstate undocumented behaviour for {API#stub}
276
- # Mocha.configure do |c|
277
- # c.reinstate_undocumented_behaviour_from_v1_9 = true
267
+ # class Example
268
+ # def foo(a, bar:); end
278
269
  # end
279
270
  #
280
- # foo = stub(:bar)
281
- # foo.inspect # => #<Mock>
282
- # foo.bar # => nil
271
+ # example = Example.new
272
+ # example.expects(:foo).with('a', bar: 'b')
273
+ # example.foo('a', { bar: 'b' })
274
+ # # This passes the test, but would result in an ArgumentError in practice
283
275
  #
284
- # @example Reinstate undocumented behaviour for {Expectation#yields}
285
- # foo = mock('foo')
286
- # foo.stubs(:my_method).yields(1, 2)
287
- # foo.my_method # => raises LocalJumpError when no block is supplied
276
+ # @example Strict keyword argument matching
288
277
  #
289
278
  # Mocha.configure do |c|
290
- # c.reinstate_undocumented_behaviour_from_v1_9 = true
279
+ # c.strict_keyword_argument_matching = true
291
280
  # end
292
281
  #
293
- # foo = mock('foo')
294
- # foo.stubs(:my_method).yields(1, 2)
295
- # foo.my_method # => does *not* raise LocalJumpError when no block is supplied
282
+ # class Example
283
+ # def foo(a, bar:); end
284
+ # end
296
285
  #
297
- def reinstate_undocumented_behaviour_from_v1_9=(value)
298
- @options[:reinstate_undocumented_behaviour_from_v1_9] = value
286
+ # example = Example.new
287
+ # example.expects(:foo).with('a', bar: 'b')
288
+ # example.foo('a', { bar: 'b' })
289
+ # # This now fails as expected
290
+ def strict_keyword_argument_matching=(value)
291
+ raise 'Strict keyword argument matching requires Ruby 2.7 and above.' unless Mocha::RUBY_V27_PLUS
292
+ @options[:strict_keyword_argument_matching] = value
299
293
  end
300
294
 
301
295
  # @private
302
- def reinstate_undocumented_behaviour_from_v1_9?
303
- @options[:reinstate_undocumented_behaviour_from_v1_9]
296
+ def strict_keyword_argument_matching?
297
+ @options[:strict_keyword_argument_matching]
304
298
  end
305
299
 
306
300
  class << self
307
- # Allow the specified +action+.
308
- #
309
- # @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+.
310
- # @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
311
- # @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+:
312
- # * {#stubbing_method_unnecessarily=}
313
- # * {#stubbing_method_on_non_mock_object=}
314
- # * {#stubbing_non_existent_method=}
315
- # * {#stubbing_non_public_method=}
316
- # * {#stubbing_method_on_nil=}
317
- def allow(action, &block)
318
- if block_given?
319
- Deprecation.warning("Use Mocha::Configuration.override(#{action}: :allow) with the same block")
320
- else
321
- Deprecation.warning("Use Mocha.configure { |c| c.#{action} = :allow }")
322
- end
323
- change_config action, :allow, &block
324
- end
325
-
326
- # @private
327
- def allow?(action)
328
- configuration.allow?(action)
329
- end
330
-
331
- # Warn if the specified +action+ is attempted.
332
- #
333
- # @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+.
334
- # @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
335
- # @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+:
336
- # * {#stubbing_method_unnecessarily=}
337
- # * {#stubbing_method_on_non_mock_object=}
338
- # * {#stubbing_non_existent_method=}
339
- # * {#stubbing_non_public_method=}
340
- # * {#stubbing_method_on_nil=}
341
- def warn_when(action, &block)
342
- if block_given?
343
- Deprecation.warning("Use Mocha::Configuration.override(#{action}: :warn) with the same block")
344
- else
345
- Deprecation.warning("Use Mocha.configure { |c| c.#{action} = :warn }")
346
- end
347
- change_config action, :warn, &block
348
- end
349
-
350
- # @private
351
- def warn_when?(action)
352
- configuration.warn_when?(action)
353
- end
354
-
355
- # Raise a {StubbingError} if the specified +action+ is attempted.
356
- #
357
- # @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+.
358
- # @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
359
- # @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+:
360
- # * {#stubbing_method_unnecessarily=}
361
- # * {#stubbing_method_on_non_mock_object=}
362
- # * {#stubbing_non_existent_method=}
363
- # * {#stubbing_non_public_method=}
364
- # * {#stubbing_method_on_nil=}
365
- def prevent(action, &block)
366
- if block_given?
367
- Deprecation.warning("Use Mocha::Configuration.override(#{action}: :prevent) with the same block")
368
- else
369
- Deprecation.warning("Use Mocha.configure { |c| c.#{action} = :prevent }")
370
- end
371
- change_config action, :prevent, &block
372
- end
373
-
374
- # @private
375
- def prevent?(action)
376
- configuration.prevent?(action)
377
- end
378
-
379
301
  # @private
380
302
  def reset_configuration
381
303
  @configuration = nil
@@ -1,3 +1,4 @@
1
+ require 'ruby2_keywords'
1
2
  require 'mocha/method_matcher'
2
3
  require 'mocha/parameters_matcher'
3
4
  require 'mocha/expectation_error'
@@ -11,6 +12,7 @@ require 'mocha/change_state_side_effect'
11
12
  require 'mocha/cardinality'
12
13
  require 'mocha/configuration'
13
14
  require 'mocha/block_matcher'
15
+ require 'mocha/backtrace_filter'
14
16
 
15
17
  module Mocha
16
18
  # Methods on expectations returned from {Mock#expects}, {Mock#stubs}, {ObjectMethods#expects} and {ObjectMethods#stubs}.
@@ -187,17 +189,26 @@ module Mocha
187
189
  at_most(1)
188
190
  end
189
191
 
190
- # Modifies expectation so that the expected method must be called with +expected_parameters+.
192
+ # Modifies expectation so that the expected method must be called with +expected_parameters_or_matchers+.
191
193
  #
192
- # May be used with parameter matchers in {ParameterMatchers}.
194
+ # May be used with Ruby literals or variables for exact matching or with parameter matchers for less-specific matching, e.g. {ParameterMatchers#includes}, {ParameterMatchers#has_key}, etc. See {ParameterMatchers} for a list of all available parameter matchers.
193
195
  #
194
- # @param [*Array] expected_parameters parameters expected.
196
+ # Positional arguments were separated from keyword arguments in Ruby v3 (see {https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0 this article}). In relation to this a new configuration option ({Configuration#strict_keyword_argument_matching=}) is available in Ruby >= 2.7.
197
+ #
198
+ # When {Configuration#strict_keyword_argument_matching=} is set to +false+ (which is currently the default), a positional +Hash+ and a set of keyword arguments passed to {#with} are treated the same for the purposes of parameter matching. However, a deprecation warning will be displayed if a positional +Hash+ matches a set of keyword arguments or vice versa. This is because {Configuration#strict_keyword_argument_matching=} will default to +true+ in the future.
199
+ #
200
+ # When {Configuration#strict_keyword_argument_matching=} is set to +true+, an actual positional +Hash+ will not match an expected set of keyword arguments; and vice versa, an actual set of keyword arguments will not match an expected positional +Hash+, i.e. the parameter matching is stricter.
201
+ #
202
+ # @see ParameterMatchers
203
+ # @see Configuration#strict_keyword_argument_matching=
204
+ #
205
+ # @param [*Array<Object,ParameterMatchers::Base>] expected_parameters_or_matchers expected parameter values or parameter matchers.
195
206
  # @yield optional block specifying custom matching.
196
- # @yieldparam [*Array] actual_parameters parameters with which expected method was invoked.
207
+ # @yieldparam [*Array<Object>] actual_parameters parameters with which expected method was invoked.
197
208
  # @yieldreturn [Boolean] +true+ if +actual_parameters+ are acceptable.
198
209
  # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
199
210
  #
200
- # @example Expected method must be called with expected parameters.
211
+ # @example Expected method must be called with exact parameter values.
201
212
  # object = mock()
202
213
  # object.expects(:expected_method).with(:param1, :param2)
203
214
  # object.expected_method(:param1, :param2)
@@ -208,6 +219,43 @@ module Mocha
208
219
  # object.expected_method(:param3)
209
220
  # # => verify fails
210
221
  #
222
+ # @example Expected method must be called with parameters matching parameter matchers.
223
+ # object = mock()
224
+ # object.expects(:expected_method).with(includes('string2'), anything)
225
+ # object.expected_method(['string1', 'string2'], 'any-old-value')
226
+ # # => verify succeeds
227
+ #
228
+ # object = mock()
229
+ # object.expects(:expected_method).with(includes('string2'), anything)
230
+ # object.expected_method(['string1'], 'any-old-value')
231
+ # # => verify fails
232
+ #
233
+ # @example Loose keyword argument matching (default)
234
+ #
235
+ # class Example
236
+ # def foo(a, bar:); end
237
+ # end
238
+ #
239
+ # example = Example.new
240
+ # example.expects(:foo).with('a', bar: 'b')
241
+ # example.foo('a', { bar: 'b' })
242
+ # # This passes the test, but would result in an ArgumentError in practice
243
+ #
244
+ # @example Strict keyword argument matching
245
+ #
246
+ # Mocha.configure do |c|
247
+ # c.strict_keyword_argument_matching = true
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 now fails as expected
258
+ #
211
259
  # @example Expected method must be called with a value divisible by 4.
212
260
  # object = mock()
213
261
  # object.expects(:expected_method).with() { |value| value % 4 == 0 }
@@ -218,10 +266,11 @@ module Mocha
218
266
  # object.expects(:expected_method).with() { |value| value % 4 == 0 }
219
267
  # object.expected_method(17)
220
268
  # # => verify fails
221
- def with(*expected_parameters, &matching_block)
222
- @parameters_matcher = ParametersMatcher.new(expected_parameters, &matching_block)
269
+ def with(*expected_parameters_or_matchers, &matching_block)
270
+ @parameters_matcher = ParametersMatcher.new(expected_parameters_or_matchers, self, &matching_block)
223
271
  self
224
272
  end
273
+ ruby2_keywords(:with)
225
274
 
226
275
  # Modifies expectation so that the expected method must be called with a block.
227
276
  #
@@ -644,5 +693,11 @@ module Mocha
644
693
  signature << " #{@block_matcher.mocha_inspect}" if @block_matcher.mocha_inspect
645
694
  signature
646
695
  end
696
+
697
+ # @private
698
+ def definition_location
699
+ filter = BacktraceFilter.new
700
+ filter.filtered(backtrace)[0]
701
+ end
647
702
  end
648
703
  end
data/lib/mocha/inspect.rb CHANGED
@@ -18,9 +18,9 @@ module Mocha
18
18
  end
19
19
 
20
20
  module HashMethods
21
- def mocha_inspect(wrapped = true)
21
+ def mocha_inspect
22
22
  unwrapped = collect { |key, value| "#{key.mocha_inspect} => #{value.mocha_inspect}" }.join(', ')
23
- wrapped ? "{#{unwrapped}}" : unwrapped
23
+ Hash.ruby2_keywords_hash?(self) ? unwrapped : "{#{unwrapped}}"
24
24
  end
25
25
  end
26
26
 
@@ -1,54 +1,26 @@
1
1
  require 'mocha/debug'
2
-
3
2
  require 'mocha/detection/mini_test'
4
-
5
- require 'mocha/integration/mini_test/nothing'
6
- require 'mocha/integration/mini_test/version_13'
7
- require 'mocha/integration/mini_test/version_140'
8
- require 'mocha/integration/mini_test/version_141'
9
- require 'mocha/integration/mini_test/version_142_to_172'
10
- require 'mocha/integration/mini_test/version_200'
11
- require 'mocha/integration/mini_test/version_201_to_222'
12
- require 'mocha/integration/mini_test/version_230_to_2101'
13
- require 'mocha/integration/mini_test/version_2110_to_2111'
14
- require 'mocha/integration/mini_test/version_2112_to_320'
15
3
  require 'mocha/integration/mini_test/adapter'
16
4
 
17
- require 'mocha/deprecation'
18
-
19
5
  module Mocha
20
6
  module Integration
21
7
  module MiniTest
22
8
  def self.activate
23
- return false unless Detection::MiniTest.testcase
24
- mini_test_version = Gem::Version.new(Detection::MiniTest.version)
9
+ target = Detection::MiniTest.testcase
10
+ return false unless target
25
11
 
12
+ mini_test_version = Gem::Version.new(Detection::MiniTest.version)
26
13
  Debug.puts "Detected MiniTest version: #{mini_test_version}"
27
14
 
28
- integration_module = [
29
- MiniTest::Adapter,
30
- MiniTest::Version2112To320,
31
- MiniTest::Version2110To2111,
32
- MiniTest::Version230To2101,
33
- MiniTest::Version201To222,
34
- MiniTest::Version200,
35
- MiniTest::Version142To172,
36
- MiniTest::Version141,
37
- MiniTest::Version140,
38
- MiniTest::Version13,
39
- MiniTest::Nothing
40
- ].detect { |m| m.applicable_to?(mini_test_version) }
15
+ unless MiniTest::Adapter.applicable_to?(mini_test_version)
16
+ raise 'Versions of minitest earlier than v3.3.0 are not supported.'
17
+ end
41
18
 
42
- target = Detection::MiniTest.testcase
43
- unless target < integration_module
44
- unless integration_module == MiniTest::Adapter
45
- Deprecation.warning(
46
- 'Versions of minitest earlier than v3.3.0 will not be supported in future versions of Mocha.'
47
- )
48
- end
49
- Debug.puts "Applying #{integration_module.description}"
50
- target.send(:include, integration_module)
19
+ unless target < MiniTest::Adapter
20
+ Debug.puts "Applying #{MiniTest::Adapter.description}"
21
+ target.send(:include, MiniTest::Adapter)
51
22
  end
23
+
52
24
  true
53
25
  end
54
26
  end
@@ -12,7 +12,7 @@ module Mocha
12
12
  include Mocha::API
13
13
 
14
14
  # @private
15
- def self.applicable_to?(test_unit_version, _ruby_version = nil)
15
+ def self.applicable_to?(test_unit_version)
16
16
  Gem::Requirement.new('>= 2.5.1').satisfied_by?(test_unit_version)
17
17
  end
18
18