mocha 1.14.0 → 2.0.0.alpha

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 (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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc815752f26d71dc07dcbe29561835e3efd005f8602ada080106a8b000491276
4
- data.tar.gz: d14e6b7be4792efe68f48eb13c084a151a6e26fe3bd30f85491a990947474ab7
3
+ metadata.gz: 69781697a086ec84aecb54fa1f63dff7842796caa275af3f01e82695e7187b0d
4
+ data.tar.gz: f0aa6493ffccdeeb7f48b0aa8a329ba57612c4c01470642b0df8f23392857534
5
5
  SHA512:
6
- metadata.gz: ca77565fde00fa34b850e95072bb32d2daf2886ddaef4ec084b6c462f4d1a8d6104ae1c5ac35f49414d3ed28527589608582b49d7ff1352388642ce710039f0d
7
- data.tar.gz: 0dab5223dde0fbd762b2a3ff92a7574f771ebcf9e8f5ec032697a3a3a7da96f6d892395b7decec1ad31512138d3f198850b5e1f20aada1885074491b13a57fb3
6
+ metadata.gz: ddc7f9eab0a9506cc0e5a69ffc0bbc359d68c4e19f2c2cd207554d2b264854f77a38bed9fa7df6f0ac110b25cf26aaa9315ec092d709a0d2cdc8ec610c3cc2fc
7
+ data.tar.gz: 7b20ce12545006706f32ff11863802dd31fe4c766925e7ca2bd63828f8dc78e98cb692d9bf5558e6ea6f401929be236c52183bc42ecfff05a752c002d9414c2c
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.8.6'
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.
data/Gemfile CHANGED
@@ -3,11 +3,7 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  # rubocop:disable Bundler/DuplicatedGem
6
- if RUBY_VERSION < '1.9.3'
7
- gem 'rake', '~> 10.0'
8
- elsif RUBY_VERSION < '2'
9
- gem 'rake', '~> 12.2.1'
10
- elsif RUBY_VERSION < '2.2'
6
+ if RUBY_VERSION < '2.2'
11
7
  gem 'rake', '~> 12.3.3'
12
8
  else
13
9
  gem 'rake'
@@ -32,3 +28,5 @@ if ENV['MOCHA_GENERATE_DOCS']
32
28
  gem 'redcarpet'
33
29
  gem 'yard'
34
30
  end
31
+
32
+ gem 'ruby2_keywords', '~> 0.0.5'
data/README.md CHANGED
@@ -338,7 +338,7 @@ $ MOCHA_GENERATE_DOCS=true GOOGLE_ANALYTICS_WEB_PROPERTY_ID=UA-625523-7 rake gen
338
338
  * Sign in to rubygems.org and find API key - https://rubygems.org/profile/edit
339
339
 
340
340
  ```bash
341
- $ curl -u james@floehopper.org https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials
341
+ $ curl -u <email-address> -H 'OTP:<one-time-password>' https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials
342
342
  ```
343
343
 
344
344
  * Release gem to Rubygems:
data/RELEASE.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # Release Notes
2
2
 
3
+ ## 2.0.0.alpha
4
+
5
+ ### External changes
6
+
7
+ * Remove support for Ruby v1.9 - thanks to @wasabigeek (#552)
8
+ * Support strict keyword argument matching - see docs for `Expectation#with` & `Configuration#strict_keyword_argument_matching=` - thanks to @wasabigeek (#446,#535,#544,#562)
9
+ * Deprecate `Hash` args that don't strictly match (#563,981c31be)
10
+ * Drop support for older versions of test-unit - gem versions of test-unit earlier than v2.5.1 and versions of test-unit
11
+ from the Ruby v1.8 standard library are no longer supported (#540,969f4845)
12
+ * Drop support for older versions of minitest - versions of minitest earlier than v3.3.0 are no longer supported (#541,ca69dc9e)
13
+ * Remove deprecated `mocha/setup.rb` mechanism (642a0ff4)
14
+ * Add missing docs for `API#stub` parameter (257b4cb4)
15
+ * Remove optional reinstatement of v1.9 behaviour (#436,#438,#569,1473ee25)
16
+ * Remove deprecated methods in `Configuration` (#421,e7ff7528)
17
+ * Fail fast when mock receives invocations in another test (#440,#442,cb054d59)
18
+ * Improve docs re using matchers in `Expectation#with` (da7237cd)
19
+ * Expand `Expectation#with` docs re keyword arguments (fed6808d)
20
+ * Improve docs for `strict_keyword_argument_matching` (8d8f881d)
21
+ * Remove deprecated Rails plugin `init.rb` file (1c617175)
22
+
23
+ ### Internal changes
24
+
25
+ * Separate linting from tests in terms of Rake tasks & CircleCI jobs - thanks to @wasabigeek (#556)
26
+ * Remove tests specific to Ruby v1.8 behaviour (46fca7ac, 3b369e99)
27
+ * Multi-line rubocop disable in `Mock#method_missing` (af2194c4)
28
+ * Remove unused arg for `HashMethods#mocha_inspect` (4f59e27f)
29
+ * Improve test runner assertions - failure vs error (eec7200a)
30
+ * Improve test coverage of `PositionalOrKeywordHash` (c294fe70)
31
+ * More consistent Test::Unit & Minitest integration (27dd3817)
32
+ * Remove redundant `require` statements (d82218a8,fa17b114)
33
+ * Add missing `require` statement (73493761)
34
+
35
+ ## 1.15.0
36
+
37
+ ### External changes
38
+
39
+ * Fix examples using mock constructor with block (1cc17667)
40
+ * Add another example for `API#sequence` (b7a7d233, #59)
41
+ * Remove support for Ruby v1.8 (ddb5d672)
42
+ * Deprecate support for Ruby versions earlier than v2.0 - thanks to @wasabigeek (#553, #555)
43
+
44
+ ### Internal changes
45
+
46
+ * Update instructions for obtaining Rubygems API key (ed9c040a)
47
+ * Consistent definitions for `respond_to?` methods (#533)
48
+ * Run test tasks before release tasks (92a1bc6e, #447)
49
+ * Fix test:performance Rake task (#538, #539)
50
+ * Tidying following removal of support for Ruby v1.8 - thanks to @nitishr (#542)
51
+ * Remove `ParametersMatcher` from `Invocation#call_description` - thanks to @wasabigeek (#543)
52
+ * Remove unnecessary splatting in Invocation - thanks to @wasabigeek (#549)
53
+ * Extract `handle_method_call` from `method_missing` - thanks to @wasabigeek (#550)
54
+
3
55
  ## 1.14.0
4
56
 
5
57
  ### External changes
data/Rakefile CHANGED
@@ -1,11 +1,18 @@
1
1
  require 'bundler'
2
- Bundler::GemHelper.install_tasks
2
+ namespace 'rubygems' do
3
+ Bundler::GemHelper.install_tasks
4
+ end
3
5
  require 'bundler/setup'
4
6
 
5
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
6
13
 
7
- desc 'Run all tests'
8
- task 'default' => ['test', 'test:performance']
14
+ desc 'Run all linters and tests'
15
+ task 'default' => ['lint', 'test', 'test:performance']
9
16
 
10
17
  desc 'Run tests'
11
18
  task 'test' do
@@ -18,15 +25,10 @@ task 'test' do
18
25
  end
19
26
 
20
27
  namespace 'test' do # rubocop:disable Metrics/BlockLength
21
- unit_tests = FileList['test/unit/**/*_test.rb']
22
- all_acceptance_tests = FileList['test/acceptance/*_test.rb']
23
- ruby186_incompatible_acceptance_tests = FileList['test/acceptance/stub_class_method_defined_on_*_test.rb'] + FileList['test/acceptance/stub_instance_method_defined_on_*_test.rb']
24
- ruby186_compatible_acceptance_tests = all_acceptance_tests - ruby186_incompatible_acceptance_tests
25
-
26
28
  desc 'Run unit tests'
27
29
  Rake::TestTask.new('units') do |t|
28
30
  t.libs << 'test'
29
- t.test_files = unit_tests
31
+ t.test_files = FileList['test/unit/**/*_test.rb']
30
32
  t.verbose = true
31
33
  t.warning = true
32
34
  end
@@ -34,11 +36,7 @@ namespace 'test' do # rubocop:disable Metrics/BlockLength
34
36
  desc 'Run acceptance tests'
35
37
  Rake::TestTask.new('acceptance') do |t|
36
38
  t.libs << 'test'
37
- t.test_files = if defined?(RUBY_VERSION) && (RUBY_VERSION >= '1.8.7')
38
- all_acceptance_tests
39
- else
40
- ruby186_compatible_acceptance_tests
41
- end
39
+ t.test_files = FileList['test/acceptance/*_test.rb']
42
40
  t.verbose = true
43
41
  t.warning = true
44
42
  end
@@ -83,13 +81,13 @@ namespace 'test' do # rubocop:disable Metrics/BlockLength
83
81
  end
84
82
  end
85
83
 
86
- begin
87
- require 'rubocop/rake_task'
88
- 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)
89
86
  RuboCop::RakeTask.new
90
- task 'test' => 'rubocop'
87
+ Rake::Task['rubocop'].invoke
88
+ else
89
+ puts 'RuboCop not available - skipping linting'
91
90
  end
92
- rescue LoadError # rubocop:disable Lint/HandleExceptions
93
91
  end
94
92
 
95
93
  # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
@@ -100,6 +98,7 @@ def benchmark_test_case(klass, iterations)
100
98
  if defined?(MiniTest)
101
99
  minitest_version = Gem::Version.new(Mocha::Detection::MiniTest.version)
102
100
  if Gem::Requirement.new('>= 5.0.0').satisfied_by?(minitest_version)
101
+ Minitest.seed = 1
103
102
  result = Benchmark.realtime { iterations.times { |_i| klass.run(MiniTest::CompositeReporter.new) } }
104
103
  MiniTest::Runnable.runnables.delete(klass)
105
104
  result
@@ -154,4 +153,4 @@ if ENV['MOCHA_GENERATE_DOCS']
154
153
  task 'generate_docs' => %w[clobber_yardoc yardoc checkout_docs_cname checkout_docs_js]
155
154
  end
156
155
 
157
- task 'release' => 'default'
156
+ task 'release' => ['default', 'rubygems:release']
@@ -4,9 +4,5 @@ gemspec :path=>"../"
4
4
 
5
5
  group :development do
6
6
  gem "rake"
7
- if RUBY_VERSION < '1.9'
8
- gem "test-unit", "~> 2"
9
- else
10
- gem "test-unit"
11
- end
7
+ gem "test-unit"
12
8
  end
@@ -1,4 +1,3 @@
1
- require 'mocha/ruby_version'
2
1
  require 'mocha/stubbed_method'
3
2
 
4
3
  module Mocha
@@ -9,10 +8,6 @@ module Mocha
9
8
  stubbee.any_instance
10
9
  end
11
10
 
12
- def method_body(method)
13
- method
14
- end
15
-
16
11
  def stubbee_method(method_name)
17
12
  stubbee.instance_method(method_name)
18
13
  end
data/lib/mocha/api.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'mocha/ruby_version'
1
2
  require 'mocha/parameter_matchers'
2
3
  require 'mocha/hooks'
3
4
  require 'mocha/mockery'
@@ -51,7 +52,6 @@ module Mocha
51
52
  #
52
53
  # @overload def mock(name)
53
54
  # @param [String, Symbol] name identifies mock object in error messages.
54
- # @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=}.
55
55
  # @overload def mock(expected_methods_vs_return_values = {})
56
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.
57
57
  # @overload def mock(name, expected_methods_vs_return_values = {})
@@ -66,26 +66,8 @@ module Mocha
66
66
  # # an error will be raised unless both Motor#start and Motor#stop have been called
67
67
  # end
68
68
  #
69
- def mock(*arguments) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
70
- if Mocha.configuration.reinstate_undocumented_behaviour_from_v1_9?
71
- if arguments.first.is_a?(Symbol)
72
- method_name = arguments[0]
73
- Deprecation.warning(
74
- "Explicitly include `#{method_name}` in Hash of expected methods vs return values,",
75
- " e.g. `mock(:#{method_name} => nil)`."
76
- )
77
- if arguments[1]
78
- Deprecation.warning(
79
- "In this case the 2nd argument for `mock(:##{method_name}, ...)` is ignored,",
80
- ' but in the future a Hash of expected methods vs return values will be respected.'
81
- )
82
- end
83
- elsif arguments.first.is_a?(String)
84
- name = arguments.shift
85
- end
86
- elsif arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
87
- name = arguments.shift
88
- end
69
+ def mock(*arguments)
70
+ name = arguments.shift.to_s if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
89
71
  expectations = arguments.shift || {}
90
72
  mock = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
91
73
  mock.expects(expectations)
@@ -98,11 +80,11 @@ module Mocha
98
80
  #
99
81
  # @overload def stub(name)
100
82
  # @param [String, Symbol] name identifies mock object in error messages.
101
- # @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=}.
102
83
  # @overload def stub(stubbed_methods_vs_return_values = {})
103
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.
104
85
  # @overload def stub(name, stubbed_methods_vs_return_values = {})
105
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.
106
88
  #
107
89
  # @example Using stubbed_methods_vs_return_values Hash to setup stubbed methods.
108
90
  # def test_motor_starts_and_stops
@@ -111,33 +93,13 @@ module Mocha
111
93
  # assert motor.stop
112
94
  # # an error will not be raised even if either Motor#start or Motor#stop has not been called
113
95
  # end
114
- # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
115
96
  def stub(*arguments)
116
- if Mocha.configuration.reinstate_undocumented_behaviour_from_v1_9?
117
- if arguments.first.is_a?(Symbol)
118
- method_name = arguments[0]
119
- Deprecation.warning(
120
- "Explicitly include `#{method_name}` in Hash of stubbed methods vs return values,",
121
- " e.g. `stub(:#{method_name} => nil)`."
122
- )
123
- if arguments[1]
124
- Deprecation.warning(
125
- "In this case the 2nd argument for `stub(:##{method_name}, ...)` is ignored,",
126
- ' but in the future a Hash of stubbed methods vs return values will be respected.'
127
- )
128
- end
129
- elsif arguments.first.is_a?(String)
130
- name = arguments.shift
131
- end
132
- elsif arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
133
- name = arguments.shift
134
- end
97
+ name = arguments.shift.to_s if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
135
98
  expectations = arguments.shift || {}
136
99
  stub = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
137
100
  stub.stubs(expectations)
138
101
  stub
139
102
  end
140
- # rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
141
103
 
142
104
  # Builds a mock object that accepts calls to any method. By default it will return +nil+ for any method call.
143
105
  #
@@ -145,7 +107,6 @@ module Mocha
145
107
  #
146
108
  # @overload def stub_everything(name)
147
109
  # @param [String, Symbol] name identifies mock object in error messages.
148
- # @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=}.
149
110
  # @overload def stub_everything(stubbed_methods_vs_return_values = {})
150
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.
151
112
  # @overload def stub_everything(name, stubbed_methods_vs_return_values = {})
@@ -159,34 +120,14 @@ module Mocha
159
120
  # assert_nil motor.irrelevant_method_2 # => no error raised
160
121
  # assert motor.stop
161
122
  # end
162
- # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
163
123
  def stub_everything(*arguments)
164
- if Mocha.configuration.reinstate_undocumented_behaviour_from_v1_9?
165
- if arguments.first.is_a?(Symbol)
166
- method_name = arguments[0]
167
- Deprecation.warning(
168
- "Explicitly include `#{method_name}` in Hash of stubbed methods vs return values,",
169
- " e.g. `stub_everything(:#{method_name} => nil)`."
170
- )
171
- if arguments[1]
172
- Deprecation.warning(
173
- "In this case the 2nd argument for `stub_everything(:##{method_name}, ...)` is ignored,",
174
- ' but in the future a Hash of stubbed methods vs return values will be respected.'
175
- )
176
- end
177
- elsif arguments.first.is_a?(String)
178
- name = arguments.shift
179
- end
180
- elsif arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
181
- name = arguments.shift
182
- end
124
+ name = arguments.shift if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
183
125
  expectations = arguments.shift || {}
184
126
  stub = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
185
127
  stub.stub_everything
186
128
  stub.stubs(expectations)
187
129
  stub
188
130
  end
189
- # rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
190
131
 
191
132
  # Builds a new sequence which can be used to constrain the order in which expectations can occur.
192
133
  #
@@ -199,11 +140,22 @@ module Mocha
199
140
  # @example Ensure methods on egg are invoked in correct order.
200
141
  # breakfast = sequence('breakfast')
201
142
  #
202
- # egg = mock('egg') do
203
- # expects(:crack).in_sequence(breakfast)
204
- # expects(:fry).in_sequence(breakfast)
205
- # expects(:eat).in_sequence(breakfast)
206
- # end
143
+ # egg = mock('egg')
144
+ # egg.expects(:crack).in_sequence(breakfast)
145
+ # egg.expects(:fry).in_sequence(breakfast)
146
+ # egg.expects(:eat).in_sequence(breakfast)
147
+ #
148
+ # @example Ensure methods across multiple objects are invoked in correct order.
149
+ # sequence = sequence(:task_order)
150
+ #
151
+ # task_one = mock("task_one")
152
+ # task_two = mock("task_two")
153
+ #
154
+ # task_one.expects(:execute).in_sequence(sequence)
155
+ # task_two.expects(:execute).in_sequence(sequence)
156
+ #
157
+ # task_one.execute
158
+ # task_two.execute
207
159
  def sequence(name)
208
160
  Sequence.new(name)
209
161
  end
@@ -226,14 +178,13 @@ module Mocha
226
178
  # @example Constrain expected invocations to occur in particular states.
227
179
  # power = states('power').starts_as('off')
228
180
  #
229
- # radio = mock('radio') do
230
- # expects(:switch_on).then(power.is('on'))
231
- # expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
232
- # expects(:adjust_volume).with(+5).when(power.is('on'))
233
- # expects(:select_channel).with('BBC World Service').when(power.is('on'))
234
- # expects(:adjust_volume).with(-5).when(power.is('on'))
235
- # expects(:switch_off).then(power.is('off'))
236
- # end
181
+ # radio = mock('radio')
182
+ # radio.expects(:switch_on).then(power.is('on'))
183
+ # radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
184
+ # radio.expects(:adjust_volume).with(+5).when(power.is('on'))
185
+ # radio.expects(:select_channel).with('BBC World Service').when(power.is('on'))
186
+ # radio.expects(:adjust_volume).with(-5).when(power.is('on'))
187
+ # radio.expects(:switch_off).then(power.is('off'))
237
188
  def states(name)
238
189
  Mockery.instance.new_state_machine(name)
239
190
  end
@@ -26,8 +26,8 @@ module Mocha
26
26
  @stubba_object
27
27
  end
28
28
 
29
- def respond_to?(method)
30
- @stubba_object.allocate.respond_to?(method.to_sym)
29
+ def respond_to?(symbol, include_all = false)
30
+ @stubba_object.allocate.respond_to?(symbol.to_sym, include_all)
31
31
  end
32
32
 
33
33
  attr_reader :stubba_object
@@ -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