spy 1.0.1 → 1.0.2
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 +4 -4
- data/lib/spy/subroutine.rb +49 -36
- data/lib/spy/version.rb +1 -1
- data/test/spy/test_subroutine.rb +80 -0
- data/test/support/pen.rb +6 -0
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3906b9e65a981746a6dc4e51106ed64fc925478cb73335eb8a9d46ae9fc31931
|
4
|
+
data.tar.gz: 85e19b544c6ccb98616dd846db8263da3d0e8abcfd2158a5749d8988c5dc32e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57544c0872ff818dc065037e1e2a10c34b1edb698ad84ebf1e184aa190d24c8663d51d011619bb6a1024c93f1d94a03a8bd6578274605a56f0b0ba06aabf6abc
|
7
|
+
data.tar.gz: 43b7ca82d10759bb6083fdd76b27941425cefbcc63bc2b8a585b34d3c8e2c2659f24c1f4f32843ee1c1b8f3aee1d64c127fb769774430e77a31ea7005aeebbcb
|
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]
|
@@ -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,17 @@ 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
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
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
|
+
if base_object.is_a? Class
|
221
|
+
call_plan(@plan, block, object, *args)
|
222
|
+
else
|
223
|
+
call_plan(@plan, block, *args)
|
224
|
+
end
|
225
|
+
end
|
246
226
|
ensure
|
247
227
|
calls << CallLog.new(object, called_from, args, block, result)
|
248
228
|
end
|
@@ -355,6 +335,39 @@ module Spy
|
|
355
335
|
@method_owner ||= current_method.owner
|
356
336
|
end
|
357
337
|
|
338
|
+
def build_call_through_plan(object)
|
339
|
+
if original_method
|
340
|
+
if @base_object.is_a?(Class) && original_method.is_a?(UnboundMethod)
|
341
|
+
original_method.bind(object)
|
342
|
+
else
|
343
|
+
original_method
|
344
|
+
end
|
345
|
+
else
|
346
|
+
Proc.new do |*args, &block|
|
347
|
+
base_object.send(:method_missing, method_name, *args, &block)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
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")
|
369
|
+
end
|
370
|
+
|
358
371
|
class << self
|
359
372
|
|
360
373
|
# retrieve the method spy from an object or create a new one
|
data/lib/spy/version.rb
CHANGED
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
|
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.2
|
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-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -169,7 +169,7 @@ homepage: https://github.com/ryanong/spy
|
|
169
169
|
licenses:
|
170
170
|
- MIT
|
171
171
|
metadata: {}
|
172
|
-
post_install_message:
|
172
|
+
post_install_message:
|
173
173
|
rdoc_options: []
|
174
174
|
require_paths:
|
175
175
|
- lib
|
@@ -185,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
185
185
|
version: '0'
|
186
186
|
requirements: []
|
187
187
|
rubygems_version: 3.1.2
|
188
|
-
signing_key:
|
188
|
+
signing_key:
|
189
189
|
specification_version: 4
|
190
190
|
summary: A simple modern mocking library that uses the spy pattern and checks method's
|
191
191
|
existence and arity.
|