spy 1.0.2 → 1.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3906b9e65a981746a6dc4e51106ed64fc925478cb73335eb8a9d46ae9fc31931
4
- data.tar.gz: 85e19b544c6ccb98616dd846db8263da3d0e8abcfd2158a5749d8988c5dc32e8
3
+ metadata.gz: 20db34080cda44a6de54d9042bd0e8081797b5418ecd31a1cf0e08aafdbeb5f4
4
+ data.tar.gz: 6c0c13d0871680db8fdf33c05e95e6484c02218cd6d3c021379297a30eac25b6
5
5
  SHA512:
6
- metadata.gz: 57544c0872ff818dc065037e1e2a10c34b1edb698ad84ebf1e184aa190d24c8663d51d011619bb6a1024c93f1d94a03a8bd6578274605a56f0b0ba06aabf6abc
7
- data.tar.gz: 43b7ca82d10759bb6083fdd76b27941425cefbcc63bc2b8a585b34d3c8e2c2659f24c1f4f32843ee1c1b8f3aee1d64c127fb769774430e77a31ea7005aeebbcb
6
+ metadata.gz: 8eb8e44ee8e3c45ce8d551e2e15db9db23f69bf64052f3514f5bffc28ee3092455d648e27ab7cbc54b702924cf1ee0b724a507d2fa37d36f6442b65b8d4af8a5
7
+ data.tar.gz: 4273aa86e5502e8de5fbc329f4c4784709e23cd392eb2455fca7baafd85026d9086568fa721f7b9c9e8e3aaa51e72042cae97059c4a2f5ed723c75de46e44c9e
@@ -0,0 +1,38 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ "master" ]
13
+ pull_request:
14
+ branches: [ "master" ]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ test:
21
+
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ matrix:
25
+ ruby-version: ['2.7', '3.0', '3.1']
26
+
27
+ steps:
28
+ - uses: actions/checkout@v3
29
+ - name: Set up Ruby
30
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
31
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
32
+ # uses: ruby/setup-ruby@v1
33
+ uses: ruby/setup-ruby@0a29871fe2b0200a17a4497bae54fe5df0d973aa # v1.115.3
34
+ with:
35
+ ruby-version: ${{ matrix.ruby-version }}
36
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
37
+ - name: Run tests
38
+ run: bundle exec rake
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 2.7.0
1
+ ruby 3.1.2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## Spy 1.0.4 (December 21th, 2022) ##
2
+
3
+ * Hash as argument got turned into keyword arguments (@svenpl)
4
+ * drop support for ruby older than 2.7 (@ryanong)
5
+
6
+ ## Spy 1.0.3 (September 15, 2022) ##
7
+
8
+ * Fix private method call errors for older ruby versions (@dylanahsmith)
9
+ * `Spy.on_instance_method` and `and_call_through` on `#to_ary-able` class causes an error (@mizukami234)
10
+ * Fix `and_return` on class method (@jfirebaugh)
11
+
12
+ ## Spy 1.0.2 (January 10, 2022) ##
13
+
14
+ * Fix subroutine call with hash as last arg (@dtheo-ad)
15
+ * Ruby 2.7+ support (@casperisfine)
16
+
1
17
  ## Spy 1.0.1 (August 20th, 2020) ##
2
18
 
3
19
  * Fix call_through w/ instance methods (@lreeves)
data/README.md CHANGED
@@ -9,8 +9,7 @@
9
9
 
10
10
  Spy is a lightweight stubbing framework with support for method spies, constant stubs, and object mocks.
11
11
 
12
- Spy supports ruby 2.1.0+.
13
- For versions less than 2.1 use v0.4.5
12
+ Spy supports ruby 2.7.0+.
14
13
 
15
14
  Spy features that were completed were tested against the rspec-mocks tests so it covers all cases that rspec-mocks does.
16
15
 
@@ -47,7 +46,7 @@ Fail faster, code faster.
47
46
  * you can usually just check the call logs.
48
47
  * if you do need to use this. It is probably a code smell. You either need to abstract your method more or add separate tests.
49
48
  * you want to use dumb double, Spy has smart mocks, they are better
50
- * you use `mock_model` and `stub_model` (I want to impliment this soon)
49
+ * you use `mock_model` and `stub_model`
51
50
 
52
51
  ## Installation
53
52
 
@@ -87,7 +86,7 @@ Spy::Subroutine.new(book, :flamethrower).hook(force:true).and_return("burnninant
87
86
  ```
88
87
 
89
88
  You can also stub instance methods of Classes and Modules. This is equivalent to
90
- rspec-mock's `Module#any_instance`
89
+ rspec-mock's `allow_any_instance_of(Module)`
91
90
 
92
91
  ```ruby
93
92
  Spy.on_instance_method(Book, :title).and_return("Cannery Row")
data/lib/spy/call_log.rb CHANGED
@@ -10,6 +10,9 @@ module Spy
10
10
  # @!attribute [r] args
11
11
  # @return [Array] arguments were sent to the method
12
12
  #
13
+ # @!attribute [r] kwargs
14
+ # @return [Array] keyword arguments were sent to the method
15
+ #
13
16
  # @!attribute [r] block
14
17
  # @return [Proc] the block that was sent to the method
15
18
  #
@@ -17,10 +20,10 @@ module Spy
17
20
  # @return The result of the method of being stubbed, or called through
18
21
 
19
22
 
20
- attr_reader :object, :called_from, :args, :block, :result
23
+ attr_reader :object, :called_from, :args, :kwargs, :block, :result
21
24
 
22
- def initialize(object, called_from, args, block, result)
23
- @object, @called_from, @args, @block, @result = object, called_from, args, block, result
25
+ def initialize(object, called_from, args, kwargs, block, result)
26
+ @object, @called_from, @args, @kwargs, @block, @result = object, called_from, args, kwargs, block, result
24
27
  end
25
28
  end
26
29
  end
@@ -84,7 +84,7 @@ module Spy
84
84
  raise NeverHookedError, "'#{method_name}' method has not been hooked" unless hooked?
85
85
 
86
86
  method_owner.send(:remove_method, method_name)
87
- if original_method && method_owner == original_method.owner
87
+ if original_method
88
88
  original_method.owner.send(:define_method, method_name, original_method)
89
89
  original_method.owner.send(original_method_visibility, method_name) if original_method_visibility
90
90
  end
@@ -201,30 +201,28 @@ module Spy
201
201
  # check if the method was called with the exact arguments
202
202
  # @param args Arguments that should have been sent to the method
203
203
  # @return [Boolean]
204
- def has_been_called_with?(*args, &block)
204
+ def has_been_called_with?(*args, **kwargs, &block)
205
205
  raise NeverHookedError unless @was_hooked
206
- match = block_given? ? block : proc { |call| call.args == args }
206
+ match = block_given? ? block : proc { |call| call.args == args && call.kwargs == kwargs }
207
207
  calls.any?(&match)
208
208
  end
209
209
 
210
210
  # invoke that the method has been called. You really shouldn't use this
211
211
  # method.
212
- def invoke(object, args, block, called_from)
213
- check_arity!(args.size)
212
+ def invoke(object, args, kwargs, block, called_from)
213
+ arity = args.size
214
+ arity += 1 if kwargs.present?
215
+ check_arity!(arity)
214
216
 
215
217
  result =
216
218
  if @call_through
217
- call_plan(build_call_through_plan(object), block, *args)
219
+ call_plan(build_call_through_plan(object), block, *args, **kwargs)
218
220
  elsif @plan
219
221
  check_for_too_many_arguments!(@plan)
220
- if base_object.is_a? Class
221
- call_plan(@plan, block, object, *args)
222
- else
223
- call_plan(@plan, block, *args)
224
- end
222
+ call_plan(@plan, block, *args, **kwargs)
225
223
  end
226
224
  ensure
227
- calls << CallLog.new(object, called_from, args, block, result)
225
+ calls << CallLog.new(object, called_from, args, kwargs, block, result)
228
226
  end
229
227
 
230
228
  # reset the call log
@@ -241,11 +239,12 @@ module Spy
241
239
  # we use eval to set the spy object id as a parameter so it can be extracted
242
240
  # and looked up later using `Method#parameters`
243
241
  SPY_ARGS_PREFIX='__spy_args_'.freeze
242
+ SPY_KWARGS_PREFIX='__spy_kwargs_'.freeze
244
243
  def override_method
245
244
  eval <<-METHOD, binding, __FILE__, __LINE__ + 1
246
245
  __method_spy__ = self
247
- lambda do |*#{SPY_ARGS_PREFIX}#{self.object_id}, &block|
248
- __method_spy__.invoke(self, #{SPY_ARGS_PREFIX}#{self.object_id}, block, caller(1)[0])
246
+ lambda do |*#{SPY_ARGS_PREFIX}#{self.object_id}, **#{SPY_KWARGS_PREFIX}#{self.object_id}, &block|
247
+ __method_spy__.invoke(self, #{SPY_ARGS_PREFIX}#{self.object_id}, #{SPY_KWARGS_PREFIX}#{self.object_id}, block, caller(1)[0])
249
248
  end
250
249
  METHOD
251
250
  end
@@ -349,23 +348,8 @@ module Spy
349
348
  end
350
349
  end
351
350
 
352
- def call_plan(plan, block, *args)
353
- if ruby_27_last_arg_hash?(args)
354
- *prefix, last = args
355
- plan.call(*prefix, **last, &block)
356
- else
357
- plan.call(*args, &block)
358
- end
359
- end
360
-
361
- # Ruby 2.7 gives a deprecation warning about passing hash as last argument for a method
362
- # with a double-splat operator (**), and Ruby 3 raises an ArgumentError exception.
363
- # This checks if args has a hash as last element to extract it and pass it with double-splat to avoid an exception.
364
- def ruby_27_last_arg_hash?(args)
365
- last = args.last
366
- last.instance_of?(Hash) &&
367
- !last.empty? &&
368
- Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
351
+ def call_plan(plan, block, *args, **kwargs)
352
+ plan.call(*args, **kwargs, &block)
369
353
  end
370
354
 
371
355
  class << self
data/lib/spy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Spy
2
- VERSION = "1.0.2"
2
+ VERSION = "1.0.4"
3
3
  end
data/spy.gemspec CHANGED
@@ -6,7 +6,7 @@ require 'spy/version'
6
6
  Gem::Specification.new do |gem|
7
7
  gem.name = "spy"
8
8
  gem.version = Spy::VERSION
9
- gem.required_ruby_version = '>= 2.1.0'
9
+ gem.required_ruby_version = '>= 2.7.0'
10
10
  gem.license = 'MIT'
11
11
  gem.authors = ["Ryan Ong"]
12
12
  gem.email = ["ryanong@gmail.com"]
@@ -0,0 +1,24 @@
1
+ require 'test_helper'
2
+
3
+ class TestClassMethod < Minitest::Test
4
+ def teardown
5
+ Spy::Agency.instance.dissolve!
6
+ end
7
+
8
+ def test_and_return
9
+ klass = Class.new do
10
+ def self.class_method(*args, &block)
11
+ end
12
+ end
13
+ received_args = nil
14
+ received_block = nil
15
+ Spy.on(klass, :class_method).and_return do |*args, &block|
16
+ received_args = args
17
+ received_block = block
18
+ end
19
+ block = -> {}
20
+ klass.class_method(:a, :b, &block)
21
+ assert_equal [:a, :b], received_args
22
+ assert_equal block, received_block
23
+ end
24
+ end
@@ -150,6 +150,14 @@ module Spy
150
150
  assert_equal string, result
151
151
  end
152
152
 
153
+ def test_spy_and_call_through_with_hash_and_keyword_args
154
+ spy_on(@pen, 'hash_and_keyword_arg').and_call_through
155
+ hsh = { hello: 'world' }
156
+
157
+ assert_equal [hsh, nil], @pen.hash_and_keyword_arg(hsh)
158
+ assert_equal [hsh, 'foo'], @pen.hash_and_keyword_arg(hsh, keyword: 'foo')
159
+ end
160
+
153
161
  def test_spy_and_call_through_returns_original_method_result
154
162
  string = "hello world"
155
163
 
@@ -237,6 +245,20 @@ module Spy
237
245
  assert pen_write_spy.has_been_called_with?("hello")
238
246
  end
239
247
 
248
+ def test_has_been_called_with_kwargs
249
+ pen_write_spy = spy_on(@pen, :opt_kwargs)
250
+ refute pen_write_spy.has_been_called_with?("hello")
251
+
252
+ @pen.opt_kwargs("hello")
253
+ assert pen_write_spy.has_been_called_with?("hello")
254
+
255
+ @pen.opt_kwargs("world", opt: "hello")
256
+ assert pen_write_spy.has_been_called_with?("world", opt: "hello")
257
+
258
+ @pen.opt_kwargs("hello world", opt: "world", opt2: "hello")
259
+ assert pen_write_spy.has_been_called_with?("hello world", opt: "world", opt2: "hello")
260
+ end
261
+
240
262
  def test_spy_hook_records_number_of_calls2
241
263
  args = ["hello world"]
242
264
  block = Proc.new {}
@@ -0,0 +1,76 @@
1
+ require 'test_helper'
2
+
3
+ module Spy
4
+ class TestUnhook < Minitest::Test
5
+ module ModuleFunctionStyle
6
+ extend self
7
+
8
+ def hello
9
+ 'hello world'
10
+ end
11
+ end
12
+
13
+ module Injected
14
+ def hello
15
+ 'hello world'
16
+ end
17
+ end
18
+
19
+ class ModuleInjectedStyle
20
+ include Injected
21
+ end
22
+
23
+ class ModuleExtendedStyle
24
+ extend Injected
25
+ end
26
+
27
+ class SingletonMethodStyle
28
+ def self.hello
29
+ 'hello world'
30
+ end
31
+ end
32
+
33
+ class InstanceMethodStyle
34
+ def hello
35
+ 'hello world'
36
+ end
37
+ end
38
+
39
+ def test_ModuleFunctionStyle
40
+ spy = Spy.on(ModuleFunctionStyle, :hello).and_return('yo')
41
+ assert_equal ModuleFunctionStyle.hello, 'yo'
42
+ spy.unhook
43
+ assert_equal ModuleFunctionStyle.hello, 'hello world'
44
+ end
45
+
46
+ def test_ModuleInjectedStyle
47
+ instance = ModuleInjectedStyle.new
48
+ spy = Spy.on(instance, :hello).and_return('yo')
49
+ assert_equal instance.hello, 'yo'
50
+ spy.unhook
51
+ assert_equal instance.hello, 'hello world'
52
+ end
53
+
54
+ def test_ModuleExtendedStyle
55
+ spy = Spy.on(ModuleExtendedStyle, :hello).and_return('yo')
56
+ assert_equal ModuleExtendedStyle.hello, 'yo'
57
+ spy.unhook
58
+ assert_equal ModuleExtendedStyle.hello, 'hello world'
59
+ end
60
+
61
+ def test_SingletonMethodStyle
62
+ spy = Spy.on(SingletonMethodStyle, :hello).and_return('yo')
63
+ assert_equal SingletonMethodStyle.hello, 'yo'
64
+ spy.unhook
65
+ assert_equal SingletonMethodStyle.hello, 'hello world'
66
+ end
67
+
68
+ def test_InstanceMethodStyle
69
+ instance = InstanceMethodStyle.new
70
+ spy = Spy.on(instance, :hello).and_return('yo')
71
+ assert_equal instance.hello, 'yo'
72
+ spy.unhook
73
+ assert_equal instance.hello, 'hello world'
74
+ end
75
+ end
76
+ end
data/test/support/pen.rb CHANGED
@@ -37,6 +37,10 @@ class Pen
37
37
  write("#{hello} #{name}")
38
38
  end
39
39
 
40
+ def hash_and_keyword_arg(hsh, keyword: nil)
41
+ [hsh, keyword]
42
+ end
43
+
40
44
  def public_method
41
45
  end
42
46
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Ong
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-10 00:00:00.000000000 Z
11
+ date: 2022-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -117,9 +117,9 @@ executables: []
117
117
  extensions: []
118
118
  extra_rdoc_files: []
119
119
  files:
120
+ - ".github/workflows/ruby.yml"
120
121
  - ".gitignore"
121
122
  - ".tool-versions"
122
- - ".travis.yml"
123
123
  - ".yardopts"
124
124
  - CHANGELOG.md
125
125
  - Gemfile
@@ -139,30 +139,16 @@ files:
139
139
  - lib/spy/nest.rb
140
140
  - lib/spy/subroutine.rb
141
141
  - lib/spy/version.rb
142
- - spec/spec_helper.rb
143
- - spec/spy/and_call_original_spec.rb
144
- - spec/spy/and_yield_spec.rb
145
- - spec/spy/any_instance_spec.rb
146
- - spec/spy/hash_excluding_matcher_spec.rb
147
- - spec/spy/hash_including_matcher_spec.rb
148
- - spec/spy/mutate_const_spec.rb
149
- - spec/spy/nil_expectation_warning_spec.rb
150
- - spec/spy/null_object_mock_spec.rb
151
- - spec/spy/partial_mock_spec.rb
152
- - spec/spy/passing_argument_matchers_spec.rb
153
- - spec/spy/serialization_spec.rb
154
- - spec/spy/stash_spec.rb
155
- - spec/spy/stub_implementation_spec.rb
156
- - spec/spy/stub_spec.rb
157
- - spec/spy/to_ary_spec.rb
158
142
  - spy.gemspec
159
143
  - test/integration/test_api.rb
144
+ - test/integration/test_class_method.rb
160
145
  - test/integration/test_constant_spying.rb
161
146
  - test/integration/test_instance_method.rb
162
147
  - test/integration/test_mocking.rb
163
148
  - test/integration/test_subroutine_spying.rb
164
149
  - test/spy/test_mock.rb
165
150
  - test/spy/test_subroutine.rb
151
+ - test/spy/test_unhook.rb
166
152
  - test/support/pen.rb
167
153
  - test/test_helper.rb
168
154
  homepage: https://github.com/ryanong/spy
@@ -177,41 +163,27 @@ required_ruby_version: !ruby/object:Gem::Requirement
177
163
  requirements:
178
164
  - - ">="
179
165
  - !ruby/object:Gem::Version
180
- version: 2.1.0
166
+ version: 2.7.0
181
167
  required_rubygems_version: !ruby/object:Gem::Requirement
182
168
  requirements:
183
169
  - - ">="
184
170
  - !ruby/object:Gem::Version
185
171
  version: '0'
186
172
  requirements: []
187
- rubygems_version: 3.1.2
173
+ rubygems_version: 3.3.17
188
174
  signing_key:
189
175
  specification_version: 4
190
176
  summary: A simple modern mocking library that uses the spy pattern and checks method's
191
177
  existence and arity.
192
178
  test_files:
193
- - spec/spec_helper.rb
194
- - spec/spy/and_call_original_spec.rb
195
- - spec/spy/and_yield_spec.rb
196
- - spec/spy/any_instance_spec.rb
197
- - spec/spy/hash_excluding_matcher_spec.rb
198
- - spec/spy/hash_including_matcher_spec.rb
199
- - spec/spy/mutate_const_spec.rb
200
- - spec/spy/nil_expectation_warning_spec.rb
201
- - spec/spy/null_object_mock_spec.rb
202
- - spec/spy/partial_mock_spec.rb
203
- - spec/spy/passing_argument_matchers_spec.rb
204
- - spec/spy/serialization_spec.rb
205
- - spec/spy/stash_spec.rb
206
- - spec/spy/stub_implementation_spec.rb
207
- - spec/spy/stub_spec.rb
208
- - spec/spy/to_ary_spec.rb
209
179
  - test/integration/test_api.rb
180
+ - test/integration/test_class_method.rb
210
181
  - test/integration/test_constant_spying.rb
211
182
  - test/integration/test_instance_method.rb
212
183
  - test/integration/test_mocking.rb
213
184
  - test/integration/test_subroutine_spying.rb
214
185
  - test/spy/test_mock.rb
215
186
  - test/spy/test_subroutine.rb
187
+ - test/spy/test_unhook.rb
216
188
  - test/support/pen.rb
217
189
  - test/test_helper.rb
data/.travis.yml DELETED
@@ -1,13 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - '2.1'
4
- - '2.5'
5
- - '2.7'
6
- - jruby
7
- - ruby-head
8
- - rbx-3
9
- matrix:
10
- allow_failures:
11
- - rvm: ruby-head
12
- - rvm: rbx-3
13
- - rvm: jruby
data/spec/spec_helper.rb DELETED
@@ -1,41 +0,0 @@
1
- require "rspec/core"
2
- require "rspec/matchers"
3
- require "rspec/expectations"
4
- require "spy"
5
- require "pry"
6
- require "pry-nav"
7
-
8
- RSpec::Matchers.define :include_method do |expected|
9
- match do |actual|
10
- actual.map { |m| m.to_s }.include?(expected.to_s)
11
- end
12
- end
13
-
14
- RSpec.configure do |config|
15
- config.color_enabled = true
16
- config.order = :random
17
- config.run_all_when_everything_filtered = true
18
- config.treat_symbols_as_metadata_keys_with_true_values = true
19
- config.filter_run_including :focus
20
- config.filter_run_excluding :broken => true
21
- config.mock_with :absolutely_nothing
22
-
23
- config.expect_with :rspec do |expectations|
24
- expectations.syntax = :expect
25
- end
26
-
27
- old_verbose = nil
28
- config.before(:each, :silence_warnings) do
29
- old_verbose = $VERBOSE
30
- $VERBOSE = nil
31
- end
32
-
33
- config.after(:each, :silence_warnings) do
34
- $VERBOSE = old_verbose
35
- end
36
-
37
- config.after(:each) do
38
- Spy.teardown
39
- end
40
- end
41
-