mocha 1.14.0 → 2.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/Gemfile +3 -5
  4. data/README.md +1 -1
  5. data/RELEASE.md +52 -0
  6. data/Rakefile +19 -20
  7. data/gemfiles/Gemfile.test-unit.latest +1 -5
  8. data/lib/mocha/any_instance_method.rb +0 -5
  9. data/lib/mocha/api.rb +29 -78
  10. data/lib/mocha/class_methods.rb +2 -2
  11. data/lib/mocha/configuration.rb +30 -108
  12. data/lib/mocha/expectation.rb +55 -7
  13. data/lib/mocha/inspect.rb +2 -2
  14. data/lib/mocha/instance_method.rb +0 -4
  15. data/lib/mocha/integration/mini_test.rb +10 -38
  16. data/lib/mocha/integration/test_unit/adapter.rb +1 -1
  17. data/lib/mocha/integration/test_unit.rb +10 -33
  18. data/lib/mocha/invocation.rb +12 -16
  19. data/lib/mocha/minitest.rb +2 -4
  20. data/lib/mocha/mock.rb +22 -26
  21. data/lib/mocha/mockery.rb +1 -3
  22. data/lib/mocha/parameter_matchers/equivalent_uri.rb +0 -1
  23. data/lib/mocha/parameter_matchers/instance_methods.rb +9 -0
  24. data/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb +56 -0
  25. data/lib/mocha/parameters_matcher.rb +0 -1
  26. data/lib/mocha/ruby_version.rb +1 -2
  27. data/lib/mocha/stubbed_method.rb +5 -42
  28. data/lib/mocha/test_unit.rb +2 -4
  29. data/lib/mocha/version.rb +1 -1
  30. data/lib/mocha.rb +0 -8
  31. data/mocha.gemspec +1 -1
  32. metadata +7 -27
  33. data/init.rb +0 -1
  34. data/lib/mocha/integration/mini_test/nothing.rb +0 -19
  35. data/lib/mocha/integration/mini_test/version_13.rb +0 -54
  36. data/lib/mocha/integration/mini_test/version_140.rb +0 -54
  37. data/lib/mocha/integration/mini_test/version_141.rb +0 -65
  38. data/lib/mocha/integration/mini_test/version_142_to_172.rb +0 -65
  39. data/lib/mocha/integration/mini_test/version_200.rb +0 -66
  40. data/lib/mocha/integration/mini_test/version_201_to_222.rb +0 -66
  41. data/lib/mocha/integration/mini_test/version_2110_to_2111.rb +0 -70
  42. data/lib/mocha/integration/mini_test/version_2112_to_320.rb +0 -73
  43. data/lib/mocha/integration/mini_test/version_230_to_2101.rb +0 -68
  44. data/lib/mocha/integration/test_unit/gem_version_200.rb +0 -62
  45. data/lib/mocha/integration/test_unit/gem_version_201_to_202.rb +0 -62
  46. data/lib/mocha/integration/test_unit/gem_version_203_to_220.rb +0 -62
  47. data/lib/mocha/integration/test_unit/gem_version_230_to_250.rb +0 -68
  48. data/lib/mocha/integration/test_unit/nothing.rb +0 -19
  49. data/lib/mocha/integration/test_unit/ruby_version_185_and_below.rb +0 -61
  50. data/lib/mocha/integration/test_unit/ruby_version_186_and_above.rb +0 -63
  51. data/lib/mocha/integration.rb +0 -11
  52. data/lib/mocha/setup.rb +0 -14
  53. data/lib/mocha/singleton_class.rb +0 -9
@@ -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'
@@ -187,17 +188,26 @@ module Mocha
187
188
  at_most(1)
188
189
  end
189
190
 
190
- # Modifies expectation so that the expected method must be called with +expected_parameters+.
191
+ # Modifies expectation so that the expected method must be called with +expected_parameters_or_matchers+.
191
192
  #
192
- # May be used with parameter matchers in {ParameterMatchers}.
193
+ # 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
194
  #
194
- # @param [*Array] expected_parameters parameters expected.
195
+ # 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.
196
+ #
197
+ # 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.
198
+ #
199
+ # 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.
200
+ #
201
+ # @see ParameterMatchers
202
+ # @see Configuration#strict_keyword_argument_matching=
203
+ #
204
+ # @param [*Array<Object,ParameterMatchers::Base>] expected_parameters_or_matchers expected parameter values or parameter matchers.
195
205
  # @yield optional block specifying custom matching.
196
- # @yieldparam [*Array] actual_parameters parameters with which expected method was invoked.
206
+ # @yieldparam [*Array<Object>] actual_parameters parameters with which expected method was invoked.
197
207
  # @yieldreturn [Boolean] +true+ if +actual_parameters+ are acceptable.
198
208
  # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
199
209
  #
200
- # @example Expected method must be called with expected parameters.
210
+ # @example Expected method must be called with exact parameter values.
201
211
  # object = mock()
202
212
  # object.expects(:expected_method).with(:param1, :param2)
203
213
  # object.expected_method(:param1, :param2)
@@ -208,6 +218,43 @@ module Mocha
208
218
  # object.expected_method(:param3)
209
219
  # # => verify fails
210
220
  #
221
+ # @example Expected method must be called with parameters matching parameter matchers.
222
+ # object = mock()
223
+ # object.expects(:expected_method).with(includes('string2'), anything)
224
+ # object.expected_method(['string1', 'string2'], 'any-old-value')
225
+ # # => verify succeeds
226
+ #
227
+ # object = mock()
228
+ # object.expects(:expected_method).with(includes('string2'), anything)
229
+ # object.expected_method(['string1'], 'any-old-value')
230
+ # # => verify fails
231
+ #
232
+ # @example Loose keyword argument matching (default)
233
+ #
234
+ # class Example
235
+ # def foo(a, bar:); end
236
+ # end
237
+ #
238
+ # example = Example.new
239
+ # example.expects(:foo).with('a', bar: 'b')
240
+ # example.foo('a', { bar: 'b' })
241
+ # # This passes the test, but would result in an ArgumentError in practice
242
+ #
243
+ # @example Strict keyword argument matching
244
+ #
245
+ # Mocha.configure do |c|
246
+ # c.strict_keyword_argument_matching = true
247
+ # end
248
+ #
249
+ # class Example
250
+ # def foo(a, bar:); end
251
+ # end
252
+ #
253
+ # example = Example.new
254
+ # example.expects(:foo).with('a', bar: 'b')
255
+ # example.foo('a', { bar: 'b' })
256
+ # # This now fails as expected
257
+ #
211
258
  # @example Expected method must be called with a value divisible by 4.
212
259
  # object = mock()
213
260
  # object.expects(:expected_method).with() { |value| value % 4 == 0 }
@@ -218,10 +265,11 @@ module Mocha
218
265
  # object.expects(:expected_method).with() { |value| value % 4 == 0 }
219
266
  # object.expected_method(17)
220
267
  # # => verify fails
221
- def with(*expected_parameters, &matching_block)
222
- @parameters_matcher = ParametersMatcher.new(expected_parameters, &matching_block)
268
+ def with(*expected_parameters_or_matchers, &matching_block)
269
+ @parameters_matcher = ParametersMatcher.new(expected_parameters_or_matchers, &matching_block)
223
270
  self
224
271
  end
272
+ ruby2_keywords(:with)
225
273
 
226
274
  # Modifies expectation so that the expected method must be called with a block.
227
275
  #
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
 
@@ -8,10 +8,6 @@ module Mocha
8
8
  stubbee
9
9
  end
10
10
 
11
- def method_body(method)
12
- PRE_RUBY_V19 ? proc { |*args, &block| method.call(*args, &block) } : method
13
- end
14
-
15
11
  def stubbee_method(method_name)
16
12
  stubbee._method(method_name)
17
13
  end
@@ -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
 
@@ -1,49 +1,26 @@
1
1
  require 'mocha/debug'
2
-
3
2
  require 'mocha/detection/test_unit'
4
-
5
- require 'mocha/integration/test_unit/nothing'
6
- require 'mocha/integration/test_unit/ruby_version_185_and_below'
7
- require 'mocha/integration/test_unit/ruby_version_186_and_above'
8
- require 'mocha/integration/test_unit/gem_version_200'
9
- require 'mocha/integration/test_unit/gem_version_201_to_202'
10
- require 'mocha/integration/test_unit/gem_version_203_to_220'
11
- require 'mocha/integration/test_unit/gem_version_230_to_250'
12
3
  require 'mocha/integration/test_unit/adapter'
13
4
 
14
- require 'mocha/deprecation'
15
-
16
5
  module Mocha
17
6
  module Integration
18
7
  module TestUnit
19
8
  def self.activate
20
- return false unless Detection::TestUnit.testcase
21
- test_unit_version = Gem::Version.new(Detection::TestUnit.version)
22
- ruby_version = Gem::Version.new(RUBY_VERSION.dup)
9
+ target = Detection::TestUnit.testcase
10
+ return false unless target
23
11
 
24
- Debug.puts "Detected Ruby version: #{ruby_version}"
12
+ test_unit_version = Gem::Version.new(Detection::TestUnit.version)
25
13
  Debug.puts "Detected Test::Unit version: #{test_unit_version}"
26
14
 
27
- integration_module = [
28
- TestUnit::Adapter,
29
- TestUnit::GemVersion230To250,
30
- TestUnit::GemVersion203To220,
31
- TestUnit::GemVersion201To202,
32
- TestUnit::GemVersion200,
33
- TestUnit::RubyVersion186AndAbove,
34
- TestUnit::RubyVersion185AndBelow,
35
- TestUnit::Nothing
36
- ].detect { |m| m.applicable_to?(test_unit_version, ruby_version) }
15
+ unless TestUnit::Adapter.applicable_to?(test_unit_version)
16
+ raise 'Versions of test-unit earlier than v2.5.1 are not supported.'
17
+ end
37
18
 
38
- unless ::Test::Unit::TestCase < integration_module
39
- unless integration_module == TestUnit::Adapter
40
- Deprecation.warning(
41
- 'Versions of test-unit earlier than v2.5.1 will not be supported in future versions of Mocha.'
42
- )
43
- end
44
- Debug.puts "Applying #{integration_module.description}"
45
- ::Test::Unit::TestCase.send(:include, integration_module)
19
+ unless target < TestUnit::Adapter
20
+ Debug.puts "Applying #{TestUnit::Adapter.description}"
21
+ target.send(:include, TestUnit::Adapter)
46
22
  end
23
+
47
24
  true
48
25
  end
49
26
  end
@@ -3,14 +3,12 @@ require 'mocha/raised_exception'
3
3
  require 'mocha/return_values'
4
4
  require 'mocha/thrown_object'
5
5
  require 'mocha/yield_parameters'
6
- require 'mocha/configuration'
7
- require 'mocha/deprecation'
8
6
 
9
7
  module Mocha
10
8
  class Invocation
11
9
  attr_reader :method_name, :block
12
10
 
13
- def initialize(mock, method_name, *arguments, &block)
11
+ def initialize(mock, method_name, arguments = [], block = nil)
14
12
  @mock = mock
15
13
  @method_name = method_name
16
14
  @arguments = arguments
@@ -22,18 +20,8 @@ module Mocha
22
20
  def call(yield_parameters = YieldParameters.new, return_values = ReturnValues.new)
23
21
  yield_parameters.next_invocation.each do |yield_args|
24
22
  @yields << ParametersMatcher.new(yield_args)
25
- if @block
26
- @block.call(*yield_args)
27
- else
28
- raise LocalJumpError unless Mocha.configuration.reinstate_undocumented_behaviour_from_v1_9?
29
- yield_args_description = ParametersMatcher.new(yield_args).mocha_inspect
30
- Deprecation.warning(
31
- "Stubbed method was instructed to yield #{yield_args_description}, but no block was given by invocation: #{call_description}.",
32
- ' This will raise a LocalJumpError in the future.',
33
- ' Use Expectation#with_block_given to constrain this expectation to match invocations supplying a block.',
34
- ' And, if necessary, add another expectation to match invocations not supplying a block.'
35
- )
36
- end
23
+ raise LocalJumpError unless @block
24
+ @block.call(*yield_args)
37
25
  end
38
26
  return_values.next(self)
39
27
  end
@@ -55,7 +43,7 @@ module Mocha
55
43
  end
56
44
 
57
45
  def call_description
58
- description = "#{@mock.mocha_inspect}.#{@method_name}#{ParametersMatcher.new(@arguments).mocha_inspect}"
46
+ description = "#{@mock.mocha_inspect}.#{@method_name}#{argument_description}"
59
47
  description << ' { ... }' unless @block.nil?
60
48
  description
61
49
  end
@@ -73,5 +61,13 @@ module Mocha
73
61
  def full_description
74
62
  "\n - #{call_description} #{result_description}"
75
63
  end
64
+
65
+ private
66
+
67
+ def argument_description
68
+ signature = arguments.mocha_inspect
69
+ signature = signature.gsub(/^\[|\]$/, '')
70
+ "(#{signature})"
71
+ end
76
72
  end
77
73
  end
@@ -1,8 +1,6 @@
1
+ require 'mocha/ruby_version'
1
2
  require 'mocha/integration/mini_test'
2
- require 'mocha/deprecation'
3
3
 
4
4
  unless Mocha::Integration::MiniTest.activate
5
- Mocha::Deprecation.warning(
6
- "MiniTest must be loaded *before* `require 'mocha/minitest'`."
7
- )
5
+ raise "MiniTest must be loaded *before* `require 'mocha/minitest'`."
8
6
  end
data/lib/mocha/mock.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'mocha/singleton_class'
1
+ require 'ruby2_keywords'
2
2
  require 'mocha/expectation'
3
3
  require 'mocha/expectation_list'
4
4
  require 'mocha/invocation'
@@ -8,7 +8,6 @@ require 'mocha/method_matcher'
8
8
  require 'mocha/parameters_matcher'
9
9
  require 'mocha/argument_iterator'
10
10
  require 'mocha/expectation_error_factory'
11
- require 'mocha/ruby_version'
12
11
 
13
12
  module Mocha
14
13
  # Traditional mock object.
@@ -165,7 +164,7 @@ module Mocha
165
164
  # @param [Array<Symbol>] method_names names of methods to unstub.
166
165
  #
167
166
  # @example Invoking an unstubbed method causes error to be raised
168
- # object = mock('mock') do
167
+ # object = mock('mock')
169
168
  # object.stubs(:stubbed_method).returns(:result_one)
170
169
  # object.stubbed_method # => :result_one
171
170
  # object.unstub(:stubbed_method)
@@ -310,10 +309,18 @@ module Mocha
310
309
  end
311
310
 
312
311
  # @private
313
- def method_missing(symbol, *arguments, &block) # rubocop:disable Style/MethodMissingSuper
312
+ # rubocop:disable Style/MethodMissingSuper
313
+ def method_missing(symbol, *arguments, &block)
314
+ handle_method_call(symbol, arguments, block)
315
+ end
316
+ ruby2_keywords(:method_missing)
317
+ # rubocop:enable Style/MethodMissingSuper
318
+
319
+ # @private
320
+ def handle_method_call(symbol, arguments, block)
314
321
  check_expiry
315
322
  check_responder_responds_to(symbol)
316
- invocation = Invocation.new(self, symbol, *arguments, &block)
323
+ invocation = Invocation.new(self, symbol, arguments, block)
317
324
  if (matching_expectation_allowing_invocation = all_expectations.match_allowing_invocation(invocation))
318
325
  matching_expectation_allowing_invocation.invoke(invocation)
319
326
  elsif (matching_expectation = all_expectations.match(invocation)) || (!matching_expectation && !@everything_stubbed)
@@ -322,25 +329,14 @@ module Mocha
322
329
  end
323
330
 
324
331
  # @private
325
- def respond_to_missing?(symbol, include_private = false)
332
+ def respond_to_missing?(symbol, include_all)
326
333
  if @responder
327
- if @responder.method(:respond_to?).arity > 1
328
- @responder.respond_to?(symbol, include_private)
329
- else
330
- @responder.respond_to?(symbol)
331
- end
334
+ @responder.respond_to?(symbol, include_all)
332
335
  else
333
336
  @everything_stubbed || all_expectations.matches_method?(symbol)
334
337
  end
335
338
  end
336
339
 
337
- if PRE_RUBY_V19
338
- # @private
339
- def respond_to?(symbol, include_private = false)
340
- respond_to_missing?(symbol, include_private)
341
- end
342
- end
343
-
344
340
  # @private
345
341
  def __verified__?(assertion_counter = nil)
346
342
  @expectations.verified?(assertion_counter)
@@ -391,14 +387,14 @@ module Mocha
391
387
  end
392
388
 
393
389
  def check_expiry
394
- if @expired # rubocop:disable Style/GuardClause
395
- Deprecation.warning(
396
- "#{mocha_inspect} was instantiated in one test but it is receiving invocations within another test.",
397
- ' This can lead to unintended interactions between tests and hence unexpected test failures.',
398
- ' Ensure that every test correctly cleans up any state that it introduces.',
399
- ' A Mocha::StubbingError will be raised in this scenario in the future.'
400
- )
401
- end
390
+ return unless @expired
391
+
392
+ sentences = [
393
+ "#{mocha_inspect} was instantiated in one test but it is receiving invocations within another test.",
394
+ 'This can lead to unintended interactions between tests and hence unexpected test failures.',
395
+ 'Ensure that every test correctly cleans up any state that it introduces.'
396
+ ]
397
+ raise StubbingError.new(sentences.join(' '), caller)
402
398
  end
403
399
  end
404
400
  end
data/lib/mocha/mockery.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'mocha/ruby_version'
2
1
  require 'mocha/central'
3
2
  require 'mocha/mock'
4
3
  require 'mocha/names'
@@ -119,10 +118,9 @@ module Mocha
119
118
  end
120
119
 
121
120
  def on_stubbing(object, method)
122
- method = PRE_RUBY_V19 ? method.to_s : method.to_sym
123
121
  signature_proc = lambda { "#{object.mocha_inspect}.#{method}" }
124
122
  check(:stubbing_non_existent_method, 'non-existent method', signature_proc) do
125
- !(object.stubba_class.__method_exists__?(method, true) || object.respond_to?(method.to_sym))
123
+ !(object.stubba_class.__method_exists__?(method, true) || object.respond_to?(method))
126
124
  end
127
125
  check(:stubbing_non_public_method, 'non-public method', signature_proc) do
128
126
  object.stubba_class.__method_exists__?(method, false)
@@ -1,4 +1,3 @@
1
- require 'mocha/deprecation'
2
1
  require 'mocha/parameter_matchers/base'
3
2
  require 'uri'
4
3
  require 'cgi'
@@ -1,4 +1,5 @@
1
1
  require 'mocha/parameter_matchers/equals'
2
+ require 'mocha/parameter_matchers/positional_or_keyword_hash'
2
3
 
3
4
  module Mocha
4
5
  module ParameterMatchers
@@ -16,3 +17,11 @@ end
16
17
  class Object
17
18
  include Mocha::ParameterMatchers::InstanceMethods
18
19
  end
20
+
21
+ # @private
22
+ class Hash
23
+ # @private
24
+ def to_matcher
25
+ Mocha::ParameterMatchers::PositionalOrKeywordHash.new(self)
26
+ end
27
+ end
@@ -0,0 +1,56 @@
1
+ require 'mocha/configuration'
2
+ require 'mocha/deprecation'
3
+ require 'mocha/parameter_matchers/base'
4
+
5
+ module Mocha
6
+ module ParameterMatchers
7
+ # @private
8
+ class PositionalOrKeywordHash < Base
9
+ def initialize(value)
10
+ @value = value
11
+ end
12
+
13
+ def matches?(available_parameters)
14
+ parameter, is_last_parameter = extract_parameter(available_parameters)
15
+ return false unless parameter == @value
16
+
17
+ if is_last_parameter && !same_type_of_hash?(parameter, @value)
18
+ return false if Mocha.configuration.strict_keyword_argument_matching?
19
+
20
+ deprecation_warning(parameter, @value) if Mocha::RUBY_V27_PLUS
21
+ end
22
+
23
+ true
24
+ end
25
+
26
+ def mocha_inspect
27
+ @value.mocha_inspect
28
+ end
29
+
30
+ private
31
+
32
+ def extract_parameter(available_parameters)
33
+ [available_parameters.shift, available_parameters.empty?]
34
+ end
35
+
36
+ def same_type_of_hash?(actual, expected)
37
+ ruby2_keywords_hash?(actual) == ruby2_keywords_hash?(expected)
38
+ end
39
+
40
+ def deprecation_warning(actual, expected)
41
+ details = "Expected #{hash_type(expected)} (#{expected.mocha_inspect}), but received #{hash_type(actual)} (#{actual.mocha_inspect})."
42
+ sentence1 = 'These will stop matching when strict keyword argument matching is enabled.'
43
+ sentence2 = 'See the documentation for Mocha::Configuration#strict_keyword_argument_matching=.'
44
+ Deprecation.warning([details, sentence1, sentence2].join(' '))
45
+ end
46
+
47
+ def hash_type(hash)
48
+ ruby2_keywords_hash?(hash) ? 'keyword arguments' : 'positional hash'
49
+ end
50
+
51
+ def ruby2_keywords_hash?(hash)
52
+ hash.is_a?(Hash) && ::Hash.ruby2_keywords_hash?(hash)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -23,7 +23,6 @@ module Mocha
23
23
  def mocha_inspect
24
24
  signature = matchers.mocha_inspect
25
25
  signature = signature.gsub(/^\[|\]$/, '')
26
- signature = signature.gsub(/^\{|\}$/, '') if matchers.length == 1
27
26
  "(#{signature})"
28
27
  end
29
28
 
@@ -1,4 +1,3 @@
1
1
  module Mocha
2
- PRE_RUBY_V19 = Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('1.9')
3
- RUBY_V2_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2')
2
+ RUBY_V27_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.7')
4
3
  end
@@ -1,5 +1,5 @@
1
+ require 'ruby2_keywords'
1
2
  require 'mocha/ruby_version'
2
- require 'mocha/singleton_class'
3
3
 
4
4
  module Mocha
5
5
  class StubbedMethod
@@ -11,7 +11,7 @@ module Mocha
11
11
  @stubbee = stubbee
12
12
  @original_method = nil
13
13
  @original_visibility = nil
14
- @method_name = PRE_RUBY_V19 ? method_name.to_s : method_name.to_sym
14
+ @method_name = method_name.to_sym
15
15
  end
16
16
 
17
17
  def stub
@@ -21,7 +21,6 @@ module Mocha
21
21
 
22
22
  def unstub
23
23
  remove_new_method
24
- restore_original_method
25
24
  mock.unstub(method_name.to_sym)
26
25
  return if mock.any_expectations?
27
26
  reset_mocha
@@ -38,28 +37,16 @@ module Mocha
38
37
  def hide_original_method
39
38
  return unless original_method_owner.__method_exists__?(method_name)
40
39
  store_original_method_visibility
41
- if use_prepended_module_for_stub_method?
42
- use_prepended_module_for_stub_method
43
- else
44
- begin
45
- store_original_method
46
- # rubocop:disable Lint/HandleExceptions
47
- rescue NameError
48
- # deal with nasties like ActiveRecord::Associations::AssociationProxy
49
- end
50
- # rubocop:enable Lint/HandleExceptions
51
- if stub_method_overwrites_original_method?
52
- remove_original_method_from_stubbee
53
- end
54
- end
40
+ use_prepended_module_for_stub_method
55
41
  end
56
42
 
57
43
  def define_new_method
58
44
  self_in_scope = self
59
45
  method_name_in_scope = method_name
60
46
  stub_method_owner.send(:define_method, method_name) do |*args, &block|
61
- self_in_scope.mock.method_missing(method_name_in_scope, *args, &block)
47
+ self_in_scope.mock.handle_method_call(method_name_in_scope, args, block)
62
48
  end
49
+ stub_method_owner.send(:ruby2_keywords, method_name)
63
50
  retain_original_visibility(stub_method_owner)
64
51
  end
65
52
 
@@ -67,18 +54,6 @@ module Mocha
67
54
  stub_method_owner.send(:remove_method, method_name)
68
55
  end
69
56
 
70
- def store_original_method
71
- @original_method = stubbee_method(method_name)
72
- end
73
-
74
- def restore_original_method
75
- return if use_prepended_module_for_stub_method?
76
- if stub_method_overwrites_original_method?
77
- original_method_owner.send(:define_method, method_name, method_body(@original_method))
78
- end
79
- retain_original_visibility(original_method_owner)
80
- end
81
-
82
57
  def matches?(other)
83
58
  return false unless other.class == self.class
84
59
  (stubbee.object_id == other.stubbee.object_id) && (method_name == other.method_name)
@@ -101,18 +76,6 @@ module Mocha
101
76
  @original_visibility = original_method_owner.__method_visibility__(method_name)
102
77
  end
103
78
 
104
- def stub_method_overwrites_original_method?
105
- @original_method && @original_method.owner == original_method_owner
106
- end
107
-
108
- def remove_original_method_from_stubbee
109
- original_method_owner.send(:remove_method, method_name)
110
- end
111
-
112
- def use_prepended_module_for_stub_method?
113
- RUBY_V2_PLUS
114
- end
115
-
116
79
  def use_prepended_module_for_stub_method
117
80
  @stub_method_owner = PrependedModule.new
118
81
  original_method_owner.__send__ :prepend, @stub_method_owner
@@ -1,8 +1,6 @@
1
+ require 'mocha/ruby_version'
1
2
  require 'mocha/integration/test_unit'
2
- require 'mocha/deprecation'
3
3
 
4
4
  unless Mocha::Integration::TestUnit.activate
5
- Mocha::Deprecation.warning(
6
- "Test::Unit must be loaded *before* `require 'mocha/test_unit'`."
7
- )
5
+ raise "Test::Unit must be loaded *before* `require 'mocha/test_unit'`."
8
6
  end
data/lib/mocha/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mocha
2
- VERSION = '1.14.0'.freeze
2
+ VERSION = '2.0.0.alpha'.freeze
3
3
  end
data/lib/mocha.rb CHANGED
@@ -1,9 +1 @@
1
1
  require 'mocha/version'
2
- require 'mocha/ruby_version'
3
- require 'mocha/deprecation'
4
-
5
- if Mocha::PRE_RUBY_V19
6
- Mocha::Deprecation.warning(
7
- 'Versions of Ruby earlier than v1.9 will not be supported in future versions of Mocha.'
8
- )
9
- end
data/mocha.gemspec CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
6
6
  s.name = 'mocha'
7
7
  s.version = Mocha::VERSION
8
8
  s.licenses = ['MIT', 'BSD-2-Clause']
9
- s.required_ruby_version = '>= 1.8.7'
9
+ s.required_ruby_version = '>= 2.0'
10
10
 
11
11
  s.authors = ['James Mead']
12
12
  s.description = 'Mocking and stubbing library with JMock/SchMock syntax, which allows mocking and stubbing of methods on real (non-mock) classes.'