spy 1.0.1 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/spy/subroutine.rb +46 -37
- data/lib/spy/version.rb +1 -1
- data/test/integration/test_class_method.rb +24 -0
- data/test/spy/test_subroutine.rb +80 -0
- data/test/spy/test_unhook.rb +76 -0
- data/test/support/pen.rb +6 -0
- metadata +10 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb02855e84ed97ae778d5983b8bba3e5839a6593416336a18240beb759f82b7f
|
4
|
+
data.tar.gz: df92498cacaab913d6d71d6d3e34778140d7367f741a1f1d750e96cbc2353a19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72e822dfc3517b8bcc4d26d9ffc0db333acd4526673a765de0a19f4396292834bd0402a307d31d6ba226c5f9017a2813fba53738d5784dc00fc0aa0e66720869
|
7
|
+
data.tar.gz: 35b2c58f79ef8da6c462ba1a5739ddc2705df1544a2f75c14ecb9731c60b5f33337f94056dbf1803480699d6729f0410985194cbfe4d376852cf5417d352b8c2
|
data/lib/spy/subroutine.rb
CHANGED
@@ -33,6 +33,7 @@ module Spy
|
|
33
33
|
@base_object, @method_name = object, method_name
|
34
34
|
@singleton_method = singleton_method
|
35
35
|
@plan = nil
|
36
|
+
@call_through = false
|
36
37
|
reset!
|
37
38
|
end
|
38
39
|
|
@@ -58,14 +59,14 @@ module Spy
|
|
58
59
|
|
59
60
|
if singleton_method
|
60
61
|
if base_object.singleton_class.method_defined?(method_name) || base_object.singleton_class.private_method_defined?(method_name)
|
61
|
-
base_object.singleton_class.alias_method
|
62
|
+
base_object.singleton_class.send(:alias_method, method_name, method_name)
|
62
63
|
end
|
63
64
|
base_object.define_singleton_method(method_name, override_method)
|
64
65
|
else
|
65
66
|
if base_object.method_defined?(method_name) || base_object.private_method_defined?(method_name)
|
66
|
-
base_object.alias_method
|
67
|
+
base_object.send(:alias_method, method_name, method_name)
|
67
68
|
end
|
68
|
-
base_object.define_method
|
69
|
+
base_object.send(:define_method, method_name, override_method)
|
69
70
|
end
|
70
71
|
|
71
72
|
if [:public, :protected, :private].include? hook_opts[:visibility]
|
@@ -83,7 +84,7 @@ module Spy
|
|
83
84
|
raise NeverHookedError, "'#{method_name}' method has not been hooked" unless hooked?
|
84
85
|
|
85
86
|
method_owner.send(:remove_method, method_name)
|
86
|
-
if original_method
|
87
|
+
if original_method
|
87
88
|
original_method.owner.send(:define_method, method_name, original_method)
|
88
89
|
original_method.owner.send(original_method_visibility, method_name) if original_method_visibility
|
89
90
|
end
|
@@ -148,28 +149,7 @@ module Spy
|
|
148
149
|
# tells the spy to call the original method
|
149
150
|
# @return [self]
|
150
151
|
def and_call_through
|
151
|
-
|
152
|
-
@plan = Proc.new do |object, *args, &block|
|
153
|
-
if original_method
|
154
|
-
if original_method.is_a? UnboundMethod
|
155
|
-
bound_method = original_method.bind(object)
|
156
|
-
bound_method.call(*args, &block)
|
157
|
-
else
|
158
|
-
original_method.call(*args, &block)
|
159
|
-
end
|
160
|
-
else
|
161
|
-
base_object.send(:method_missing, method_name, *args, &block)
|
162
|
-
end
|
163
|
-
end
|
164
|
-
else
|
165
|
-
@plan = Proc.new do |*args, &block|
|
166
|
-
if original_method
|
167
|
-
original_method.call(*args, &block)
|
168
|
-
else
|
169
|
-
base_object.send(:method_missing, method_name, *args, &block)
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
152
|
+
@call_through = true
|
173
153
|
|
174
154
|
self
|
175
155
|
end
|
@@ -232,17 +212,13 @@ module Spy
|
|
232
212
|
def invoke(object, args, block, called_from)
|
233
213
|
check_arity!(args.size)
|
234
214
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
check_for_too_many_arguments!(@plan)
|
243
|
-
@plan.call(*args, &block)
|
244
|
-
end
|
245
|
-
end
|
215
|
+
result =
|
216
|
+
if @call_through
|
217
|
+
call_plan(build_call_through_plan(object), block, *args)
|
218
|
+
elsif @plan
|
219
|
+
check_for_too_many_arguments!(@plan)
|
220
|
+
call_plan(@plan, block, *args)
|
221
|
+
end
|
246
222
|
ensure
|
247
223
|
calls << CallLog.new(object, called_from, args, block, result)
|
248
224
|
end
|
@@ -355,6 +331,39 @@ module Spy
|
|
355
331
|
@method_owner ||= current_method.owner
|
356
332
|
end
|
357
333
|
|
334
|
+
def build_call_through_plan(object)
|
335
|
+
if original_method
|
336
|
+
if @base_object.is_a?(Class) && original_method.is_a?(UnboundMethod)
|
337
|
+
original_method.bind(object)
|
338
|
+
else
|
339
|
+
original_method
|
340
|
+
end
|
341
|
+
else
|
342
|
+
Proc.new do |*args, &block|
|
343
|
+
base_object.send(:method_missing, method_name, *args, &block)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def call_plan(plan, block, *args)
|
349
|
+
if ruby_27_last_arg_hash?(args)
|
350
|
+
*prefix, last = args
|
351
|
+
plan.call(*prefix, **last, &block)
|
352
|
+
else
|
353
|
+
plan.call(*args, &block)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
# Ruby 2.7 gives a deprecation warning about passing hash as last argument for a method
|
358
|
+
# with a double-splat operator (**), and Ruby 3 raises an ArgumentError exception.
|
359
|
+
# This checks if args has a hash as last element to extract it and pass it with double-splat to avoid an exception.
|
360
|
+
def ruby_27_last_arg_hash?(args)
|
361
|
+
last = args.last
|
362
|
+
last.instance_of?(Hash) &&
|
363
|
+
!last.empty? &&
|
364
|
+
Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
|
365
|
+
end
|
366
|
+
|
358
367
|
class << self
|
359
368
|
|
360
369
|
# retrieve the method spy from an object or create a new one
|
data/lib/spy/version.rb
CHANGED
@@ -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
|
data/test/spy/test_subroutine.rb
CHANGED
@@ -6,6 +6,10 @@ module Spy
|
|
6
6
|
Subroutine.new(base_object, method_name).hook
|
7
7
|
end
|
8
8
|
|
9
|
+
def spy_on_instance_method(base_object, method_name)
|
10
|
+
Subroutine.new(base_object, method_name, false).hook
|
11
|
+
end
|
12
|
+
|
9
13
|
def setup
|
10
14
|
@pen = Pen.new
|
11
15
|
end
|
@@ -109,6 +113,15 @@ module Spy
|
|
109
113
|
assert_empty @pen.written
|
110
114
|
end
|
111
115
|
|
116
|
+
def test_spy_and_return_can_call_a_block_with_hash
|
117
|
+
result = "hello world"
|
118
|
+
|
119
|
+
spy_on(@pen, :write_hash).and_return { |**opts| opts[:test] }
|
120
|
+
|
121
|
+
assert_equal result, @pen.write_hash(test: result)
|
122
|
+
assert_empty @pen.written
|
123
|
+
end
|
124
|
+
|
112
125
|
def test_spy_and_return_can_call_a_block_raises_when_there_is_an_arity_mismatch
|
113
126
|
write_spy = spy_on(@pen, :write)
|
114
127
|
write_spy.and_return do |*args|
|
@@ -137,6 +150,73 @@ module Spy
|
|
137
150
|
assert_equal string, result
|
138
151
|
end
|
139
152
|
|
153
|
+
def test_spy_and_call_through_returns_original_method_result
|
154
|
+
string = "hello world"
|
155
|
+
|
156
|
+
write_spy = spy_on(@pen, :write).and_call_through
|
157
|
+
another_spy = spy_on(@pen, :another).and_call_through
|
158
|
+
|
159
|
+
result = @pen.write(string)
|
160
|
+
|
161
|
+
assert_equal string, result
|
162
|
+
assert write_spy.has_been_called?
|
163
|
+
assert_equal 'another', @pen.another
|
164
|
+
assert another_spy.has_been_called?
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_spy_and_call_through_with_hash_original_method
|
168
|
+
string = 'test:hello world'
|
169
|
+
|
170
|
+
write_spy = spy_on(@pen, :write_hash).and_call_through
|
171
|
+
|
172
|
+
@pen.write_hash(test: 'hello world')
|
173
|
+
assert_equal string, @pen.written.last
|
174
|
+
assert write_spy.has_been_called?
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_spy_on_instance_and_call_through_returns_original_method_result
|
178
|
+
string = "hello world"
|
179
|
+
|
180
|
+
inst_write_spy = spy_on_instance_method(Pen, :write).and_call_through
|
181
|
+
inst_another_spy = spy_on_instance_method(Pen, :another).and_call_through
|
182
|
+
|
183
|
+
result = @pen.write(string)
|
184
|
+
|
185
|
+
assert_equal string, result
|
186
|
+
assert inst_write_spy.has_been_called?
|
187
|
+
assert_equal 'another', @pen.another
|
188
|
+
assert inst_another_spy.has_been_called?
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_spy_on_instance_and_call_through_with_hash
|
192
|
+
string = 'test:hello world'
|
193
|
+
|
194
|
+
inst_write_spy = spy_on_instance_method(Pen, :write_hash).and_call_through
|
195
|
+
|
196
|
+
@pen.write_hash(test: 'hello world')
|
197
|
+
|
198
|
+
assert_equal string, @pen.written.last
|
199
|
+
assert inst_write_spy.has_been_called?
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_spy_on_instance_and_call_through_to_aryable
|
203
|
+
to_aryable = Class.new do
|
204
|
+
def hello
|
205
|
+
'hello'
|
206
|
+
end
|
207
|
+
|
208
|
+
def to_ary
|
209
|
+
[1]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
inst_hello_spy = spy_on_instance_method(to_aryable, :hello).and_call_through
|
214
|
+
inst = to_aryable.new
|
215
|
+
|
216
|
+
assert_equal 'hello', inst.hello
|
217
|
+
assert inst_hello_spy.has_been_called?
|
218
|
+
end
|
219
|
+
|
140
220
|
def test_spy_hook_records_number_of_calls
|
141
221
|
pen_write_spy = spy_on(@pen, :write)
|
142
222
|
assert_equal 0, pen_write_spy.calls.size
|
@@ -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
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.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Ong
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-09-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -157,19 +157,21 @@ files:
|
|
157
157
|
- spec/spy/to_ary_spec.rb
|
158
158
|
- spy.gemspec
|
159
159
|
- test/integration/test_api.rb
|
160
|
+
- test/integration/test_class_method.rb
|
160
161
|
- test/integration/test_constant_spying.rb
|
161
162
|
- test/integration/test_instance_method.rb
|
162
163
|
- test/integration/test_mocking.rb
|
163
164
|
- test/integration/test_subroutine_spying.rb
|
164
165
|
- test/spy/test_mock.rb
|
165
166
|
- test/spy/test_subroutine.rb
|
167
|
+
- test/spy/test_unhook.rb
|
166
168
|
- test/support/pen.rb
|
167
169
|
- test/test_helper.rb
|
168
170
|
homepage: https://github.com/ryanong/spy
|
169
171
|
licenses:
|
170
172
|
- MIT
|
171
173
|
metadata: {}
|
172
|
-
post_install_message:
|
174
|
+
post_install_message:
|
173
175
|
rdoc_options: []
|
174
176
|
require_paths:
|
175
177
|
- lib
|
@@ -184,8 +186,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
184
186
|
- !ruby/object:Gem::Version
|
185
187
|
version: '0'
|
186
188
|
requirements: []
|
187
|
-
rubygems_version: 3.
|
188
|
-
signing_key:
|
189
|
+
rubygems_version: 3.3.17
|
190
|
+
signing_key:
|
189
191
|
specification_version: 4
|
190
192
|
summary: A simple modern mocking library that uses the spy pattern and checks method's
|
191
193
|
existence and arity.
|
@@ -207,11 +209,13 @@ test_files:
|
|
207
209
|
- spec/spy/stub_spec.rb
|
208
210
|
- spec/spy/to_ary_spec.rb
|
209
211
|
- test/integration/test_api.rb
|
212
|
+
- test/integration/test_class_method.rb
|
210
213
|
- test/integration/test_constant_spying.rb
|
211
214
|
- test/integration/test_instance_method.rb
|
212
215
|
- test/integration/test_mocking.rb
|
213
216
|
- test/integration/test_subroutine_spying.rb
|
214
217
|
- test/spy/test_mock.rb
|
215
218
|
- test/spy/test_subroutine.rb
|
219
|
+
- test/spy/test_unhook.rb
|
216
220
|
- test/support/pen.rb
|
217
221
|
- test/test_helper.rb
|