spy 1.0.1 → 1.0.2
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 +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.
|