much-stub 0.1.1 → 0.1.6
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/Gemfile +3 -1
- data/README.md +289 -39
- data/lib/much-stub.rb +106 -41
- data/lib/much-stub/call.rb +17 -0
- data/lib/much-stub/call_spy.rb +124 -0
- data/lib/much-stub/version.rb +3 -1
- data/much-stub.gemspec +4 -3
- data/test/helper.rb +5 -3
- data/test/support/factory.rb +3 -2
- data/test/system/much-stub_tests.rb +206 -223
- data/test/unit/call_spy_tests.rb +110 -0
- data/test/unit/call_tests.rb +70 -0
- data/test/unit/much-stub_tests.rb +185 -100
- metadata +13 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6dc9e4397c7c270a2678eac9f4761adb80bdd842e6708417844855639c6acfa
|
4
|
+
data.tar.gz: 57f89b55ddaf3b17a2444f7bbd40d2e8f9b1ad3046d1634021412f8847df605b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b57f84be1092bba24dfd51ace398e8116941a16e17f8698436158309305672e55076b46bb4b5695763c97530f7cbfce6f974de6b331432336d7b0aad8a2ecfe0
|
7
|
+
data.tar.gz: 4705afaee215d93ed0a998949dd70dd2d13f03aa5fac9a31093940be16799ad43b2878df7870a046b79a1a4e2c9625bb0f5195896c371c24728600445c18c55a
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -16,77 +16,327 @@ Note: this was originally implemented in and extracted from [Assert](https://git
|
|
16
16
|
```ruby
|
17
17
|
# Given this object/API
|
18
18
|
|
19
|
-
|
20
|
-
def
|
21
|
-
|
19
|
+
my_class = Class.new do
|
20
|
+
def my_method
|
21
|
+
"my-method"
|
22
|
+
end
|
23
|
+
|
24
|
+
def my_value(value)
|
25
|
+
value
|
26
|
+
end
|
22
27
|
end
|
23
|
-
|
28
|
+
my_object = my_class.new
|
24
29
|
|
25
|
-
|
26
|
-
# =>
|
27
|
-
|
30
|
+
my_object.my_method
|
31
|
+
# => "my-method"
|
32
|
+
my_object.my_value(123)
|
28
33
|
# => 123
|
29
|
-
|
34
|
+
my_object.my_value(456)
|
30
35
|
# => 456
|
31
36
|
|
32
|
-
# Create a new stub for the :
|
37
|
+
# Create a new stub for the :my_method method
|
33
38
|
|
34
|
-
MuchStub.(
|
35
|
-
|
36
|
-
# => StubError: `
|
37
|
-
MuchStub.(
|
38
|
-
|
39
|
-
# =>
|
40
|
-
|
39
|
+
MuchStub.(my_object, :my_method)
|
40
|
+
my_object.my_method
|
41
|
+
# => StubError: `my_method` not stubbed.
|
42
|
+
MuchStub.(my_object, :my_method){ "stubbed-method" }
|
43
|
+
my_object.my_method
|
44
|
+
# => "stubbed-method"
|
45
|
+
my_object.my_method(123)
|
41
46
|
# => StubError: arity mismatch
|
42
|
-
MuchStub.(
|
47
|
+
MuchStub.(my_object, :my_method).with(123){ "stubbed-method" }
|
43
48
|
# => StubError: arity mismatch
|
44
|
-
MuchStub.stub_send(myobj, :mymeth) # call to the original method post-stub
|
45
|
-
# => 'meth'
|
46
49
|
|
47
|
-
#
|
50
|
+
# Call the original method after it has been stubbed.
|
51
|
+
|
52
|
+
MuchStub.stub_send(my_object, :my_method)
|
53
|
+
# => "my-method"
|
54
|
+
|
55
|
+
# Create a new stub for the :my_value method
|
48
56
|
|
49
|
-
MuchStub.(
|
57
|
+
MuchStub.(my_object, :my_value){ "stubbed-method" }
|
50
58
|
# => StubError: arity mismatch
|
51
|
-
MuchStub.(
|
52
|
-
|
59
|
+
MuchStub.(my_object, :my_value).with(123){ |val| val.to_s }
|
60
|
+
my_object.my_value
|
53
61
|
# => StubError: arity mismatch
|
54
|
-
|
55
|
-
# =>
|
56
|
-
|
57
|
-
# => StubError: `
|
62
|
+
my_object.my_value(123)
|
63
|
+
# => "123"
|
64
|
+
my_object.my_value(456)
|
65
|
+
# => StubError: `my_value(456)` not stubbed.
|
58
66
|
|
59
|
-
# Call
|
67
|
+
# Call the original method after it has been stubbed.
|
60
68
|
|
61
|
-
MuchStub.stub_send(
|
69
|
+
MuchStub.stub_send(my_object, :my_value, 123)
|
62
70
|
# => 123
|
63
|
-
MuchStub.stub_send(
|
71
|
+
MuchStub.stub_send(my_object, :my_value, 456)
|
64
72
|
# => 456
|
65
73
|
|
66
74
|
# Unstub individual stubs
|
67
75
|
|
68
|
-
MuchStub.unstub(
|
69
|
-
MuchStub.unstub(
|
76
|
+
MuchStub.unstub(my_object, :my_method)
|
77
|
+
MuchStub.unstub(my_object, :my_value)
|
70
78
|
|
71
79
|
# OR blanket unstub all stubs
|
72
80
|
|
73
81
|
MuchStub.unstub!
|
74
82
|
|
75
|
-
#
|
83
|
+
# The original API/behavior is preserved after unstubbing
|
76
84
|
|
77
|
-
|
78
|
-
# =>
|
79
|
-
|
85
|
+
my_object.my_method
|
86
|
+
# => "my-method"
|
87
|
+
my_object.my_value(123)
|
80
88
|
# => 123
|
81
|
-
|
89
|
+
my_object.my_value(456)
|
82
90
|
# => 456
|
83
91
|
```
|
84
92
|
|
93
|
+
### Stubs for spying
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
# Given this object/API
|
97
|
+
|
98
|
+
my_class = Class.new do
|
99
|
+
def basic_method(value)
|
100
|
+
value
|
101
|
+
end
|
102
|
+
|
103
|
+
def iterator_method(items, &block)
|
104
|
+
items.each(&block)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
my_object = my_class.new
|
108
|
+
|
109
|
+
# Store method call arguments/blocks for spying.
|
110
|
+
|
111
|
+
basic_method_called_with = nil
|
112
|
+
MuchStub.(my_object, :basic_method) { |*args|
|
113
|
+
basic_method_called_with = MuchStub::Call.new(*args)
|
114
|
+
}
|
115
|
+
# OR
|
116
|
+
MuchStub.(my_object, :basic_method).on_call { |call|
|
117
|
+
basic_method_called_with = call
|
118
|
+
}
|
119
|
+
# OR
|
120
|
+
MuchStub.on_call(my_object, :basic_method) { |call|
|
121
|
+
# MucStub.on_call(...) { ... } is equivalent to
|
122
|
+
# MuchStub.(...).on_call { ... }
|
123
|
+
basic_method_called_with = call
|
124
|
+
}
|
125
|
+
|
126
|
+
my_object.basic_method(123)
|
127
|
+
basic_method_called_with.args
|
128
|
+
# => [123]
|
129
|
+
|
130
|
+
basic_method_called_with = nil
|
131
|
+
MuchStub.(my_object, :basic_method).with(4, 5, 6) { |*args|
|
132
|
+
basic_method_called_with = MuchStub::Call.new(*args)
|
133
|
+
}
|
134
|
+
# OR
|
135
|
+
MuchStub.(my_object, :basic_method).with(4, 5, 6).on_call { |call|
|
136
|
+
basic_method_called_with = call
|
137
|
+
}
|
138
|
+
|
139
|
+
my_object.basic_method(4, 5, 6)
|
140
|
+
basic_method_called_with.args
|
141
|
+
# => [4,5,6]
|
142
|
+
|
143
|
+
iterator_method_called_with = nil
|
144
|
+
MuchStub.(my_object, :iterator_method) { |*args, &block|
|
145
|
+
iterator_method_called_with = MuchStub::Call.new(*args)
|
146
|
+
}
|
147
|
+
# OR
|
148
|
+
MuchStub.(my_object, :iterator_method).on_call { |call|
|
149
|
+
iterator_method_called_with = call
|
150
|
+
}
|
151
|
+
|
152
|
+
my_object.iterator_method([1, 2, 3], &:to_s)
|
153
|
+
iterator_method_called_with.args
|
154
|
+
# => [[1, 2, 3]]
|
155
|
+
iterator_method_called_with.block
|
156
|
+
# => #<Proc:0x00007fb083a6feb0(&:to_s)>
|
157
|
+
|
158
|
+
# Count method calls for spying.
|
159
|
+
|
160
|
+
basic_method_call_count = 0
|
161
|
+
MuchStub.(my_object, :basic_method) {
|
162
|
+
basic_method_call_count += 1
|
163
|
+
}
|
164
|
+
|
165
|
+
my_object.basic_method(123)
|
166
|
+
basic_method_call_count
|
167
|
+
# => 1
|
168
|
+
|
169
|
+
# Count method calls and store arguments for spying.
|
170
|
+
|
171
|
+
basic_method_calls = []
|
172
|
+
MuchStub.(my_object, :basic_method) { |*args|
|
173
|
+
basic_method_calls << MuchStub::Call.new(*args)
|
174
|
+
}
|
175
|
+
# OR
|
176
|
+
MuchStub.(my_object, :basic_method).on_call { |call|
|
177
|
+
basic_method_calls << call
|
178
|
+
}
|
179
|
+
|
180
|
+
my_object.basic_method(123)
|
181
|
+
basic_method_calls.size
|
182
|
+
# => 1
|
183
|
+
basic_method_calls.first.args
|
184
|
+
# => [123]
|
185
|
+
```
|
186
|
+
|
187
|
+
### Stubs for test doubles.
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
# Given this object/API ...
|
191
|
+
|
192
|
+
my_class = Class.new do
|
193
|
+
def build_thing(thing_value);
|
194
|
+
Thing.new(value)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
my_object = my_class.new
|
198
|
+
|
199
|
+
# ... and this Test Double.
|
200
|
+
class FakeThing
|
201
|
+
attr_reader :built_with
|
202
|
+
|
203
|
+
def initialize(*args)
|
204
|
+
@built_with = args
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Stub in the test double.
|
209
|
+
|
210
|
+
MuchStub.(my_object, :build_thing) { |*args|
|
211
|
+
FakeThing.new(*args)
|
212
|
+
}
|
213
|
+
|
214
|
+
thing = my_object.build_thing(123)
|
215
|
+
thing.built_with
|
216
|
+
# => [123]
|
217
|
+
```
|
218
|
+
|
219
|
+
### `MuchStub.tap`
|
220
|
+
|
221
|
+
Use the `.tap` method to spy on method calls while preserving the original method return value and behavior.
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
# Given this object/API
|
225
|
+
|
226
|
+
my_class = Class.new do
|
227
|
+
def basic_method(value)
|
228
|
+
value.to_s
|
229
|
+
end
|
230
|
+
end
|
231
|
+
my_object = my_class.new
|
232
|
+
|
233
|
+
# Normal stubs override the original behavior and return value...
|
234
|
+
basic_method_called_with = nil
|
235
|
+
MuchStub.(my_object, :basic_method) { |*args|
|
236
|
+
basic_method_called_with = args
|
237
|
+
}
|
238
|
+
|
239
|
+
# ... in this case not converting the value to a String and returning it and
|
240
|
+
# instead returning the arguments passed to the method.
|
241
|
+
my_object.basic_method(123)
|
242
|
+
# => [123]
|
243
|
+
basic_method_called_with
|
244
|
+
# => [123]
|
245
|
+
|
246
|
+
# Use `MuchStub.tap` to preserve the methods behavior and also spy.
|
247
|
+
|
248
|
+
basic_method_called_with = nil
|
249
|
+
MuchStub.tap(my_object, :basic_method) { |value, *args|
|
250
|
+
basic_method_called_with = MuchStub::Call.new(*args)
|
251
|
+
}
|
252
|
+
# OR
|
253
|
+
MuchStub.tap_on_call(my_object, :basic_method) { |value, call|
|
254
|
+
basic_method_called_with = call
|
255
|
+
}
|
256
|
+
|
257
|
+
my_object.basic_method(123)
|
258
|
+
# => "123"
|
259
|
+
basic_method_called_with.args
|
260
|
+
# => [123]
|
261
|
+
```
|
262
|
+
|
263
|
+
#### Late-bound stubs using `MuchStub.tap`
|
264
|
+
|
265
|
+
Use the `.tap` method to stub any return values of method calls.
|
266
|
+
|
267
|
+
```ruby
|
268
|
+
# Given:
|
269
|
+
|
270
|
+
class Thing
|
271
|
+
attr_reader :value
|
272
|
+
|
273
|
+
def initialize(value)
|
274
|
+
@value = value
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
my_class = Class.new do
|
279
|
+
def thing(value)
|
280
|
+
Thing.new(value)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
my_object = my_class.new
|
284
|
+
|
285
|
+
# Use `MuchStub.tap` to stub any thing instances created by `my_object.thing`
|
286
|
+
# (and also spy on the call arguments)
|
287
|
+
|
288
|
+
thing_built_with = nil
|
289
|
+
MuchStub.tap(my_object, :thing) { |thing, *args|
|
290
|
+
thing_built_with = args
|
291
|
+
MuchStub.(thing, :value) { 456 }
|
292
|
+
}
|
293
|
+
|
294
|
+
thing = my_object.thing(123)
|
295
|
+
# => #<Thing:0x00007fd5ca9df510 @value=123>
|
296
|
+
thing_built_with
|
297
|
+
# => [123]
|
298
|
+
thing.value
|
299
|
+
# => 456
|
300
|
+
```
|
301
|
+
|
302
|
+
### `MuchStub.spy`
|
303
|
+
|
304
|
+
Use the `.spy` method to spy on method calls. This is especially helpful for spying on _chained_ method calls.
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
# Given this object/API
|
308
|
+
|
309
|
+
myclass = Class.new do
|
310
|
+
def one; self; end
|
311
|
+
def two(val); self; end
|
312
|
+
def three; self; end
|
313
|
+
def ready?; false; end
|
314
|
+
end
|
315
|
+
myobj = myclass.new
|
316
|
+
|
317
|
+
spy =
|
318
|
+
MuchStub.spy(myobj :one, :two, :three, ready?: true)
|
319
|
+
|
320
|
+
assert_equal spy, myobj.one
|
321
|
+
assert_equal spy, myobj.two("a")
|
322
|
+
assert_equal spy, myobj.three
|
323
|
+
|
324
|
+
assert_true myobj.one.two("b").three.ready?
|
325
|
+
|
326
|
+
assert_kind_of MuchStub::CallSpy, spy
|
327
|
+
assert_equal 2, spy.one_call_count
|
328
|
+
assert_equal 2, spy.two_call_count
|
329
|
+
assert_equal 2, spy.three_call_count
|
330
|
+
assert_equal 1, spy.ready_predicate_call_count
|
331
|
+
assert_equal ["b"], spy.two_last_called_with.args
|
332
|
+
assert_true spy.ready_predicate_called?
|
333
|
+
```
|
334
|
+
|
85
335
|
## Installation
|
86
336
|
|
87
337
|
Add this line to your application's Gemfile:
|
88
338
|
|
89
|
-
gem
|
339
|
+
gem "much-stub"
|
90
340
|
|
91
341
|
And then execute:
|
92
342
|
|
@@ -100,6 +350,6 @@ Or install it yourself as:
|
|
100
350
|
|
101
351
|
1. Fork it
|
102
352
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
103
|
-
3. Commit your changes (`git commit -am
|
353
|
+
3. Commit your changes (`git commit -am "Added some feature"`)
|
104
354
|
4. Push to the branch (`git push origin my-new-feature`)
|
105
355
|
5. Create new Pull Request
|
data/lib/much-stub.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "much-stub/version"
|
2
4
|
|
3
|
-
|
5
|
+
require "much-stub/call"
|
6
|
+
require "much-stub/call_spy"
|
4
7
|
|
8
|
+
module MuchStub
|
5
9
|
def self.stubs
|
6
10
|
@stubs ||= {}
|
7
11
|
end
|
@@ -10,8 +14,10 @@ module MuchStub
|
|
10
14
|
MuchStub::Stub.key(obj, meth)
|
11
15
|
end
|
12
16
|
|
13
|
-
def self.
|
14
|
-
|
17
|
+
def self.arity_matches?(method, args)
|
18
|
+
return true if method.arity == args.size # mandatory args
|
19
|
+
return true if method.arity < 0 && args.size >= (method.arity+1).abs # variable args
|
20
|
+
return false
|
15
21
|
end
|
16
22
|
|
17
23
|
def self.stub(obj, meth, &block)
|
@@ -20,6 +26,18 @@ module MuchStub
|
|
20
26
|
self.stubs[key].tap{ |s| s.do = block }
|
21
27
|
end
|
22
28
|
|
29
|
+
def self.call(*args, &block)
|
30
|
+
self.stub(*args, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.stub_on_call(*args, &on_call_block)
|
34
|
+
self.stub(*args).on_call(&on_call_block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.on_call(*args, &on_call_block)
|
38
|
+
self.stub_on_call(*args, &on_call_block)
|
39
|
+
end
|
40
|
+
|
23
41
|
def self.unstub(obj, meth)
|
24
42
|
key = self.stub_key(obj, meth)
|
25
43
|
(self.stubs.delete(key) || MuchStub::NullStub.new).teardown
|
@@ -37,8 +55,31 @@ module MuchStub
|
|
37
55
|
stub.call_method(args, &block)
|
38
56
|
end
|
39
57
|
|
40
|
-
|
58
|
+
def self.tap(obj, meth, &tap_block)
|
59
|
+
self.stub(obj, meth) { |*args, &block|
|
60
|
+
self.stub_send(obj, meth, *args, &block).tap { |value|
|
61
|
+
tap_block.call(value, *args, &block) if tap_block
|
62
|
+
}
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.tap_on_call(obj, meth, &on_call_block)
|
67
|
+
self.tap(obj, meth) { |value, *args, &block|
|
68
|
+
on_call_block.call(value, MuchStub::Call.new(*args, &block)) if on_call_block
|
69
|
+
}
|
70
|
+
end
|
41
71
|
|
72
|
+
def self.spy(obj, *meths, **return_values)
|
73
|
+
MuchStub::CallSpy.new(**return_values).call_spy_tap do |spy|
|
74
|
+
meths.each do |meth|
|
75
|
+
self.stub(obj, meth) { |*args, &block|
|
76
|
+
spy.__send__(meth, *args, &block)
|
77
|
+
}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Stub
|
42
83
|
def self.key(object, method_name)
|
43
84
|
"--#{object.object_id}--#{method_name}--"
|
44
85
|
end
|
@@ -69,11 +110,13 @@ module MuchStub
|
|
69
110
|
|
70
111
|
def call(args, orig_caller = nil, &block)
|
71
112
|
orig_caller ||= caller_locations
|
72
|
-
unless arity_matches?(args)
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
113
|
+
unless MuchStub.arity_matches?(@method, args)
|
114
|
+
raise(
|
115
|
+
StubArityError.new(
|
116
|
+
@method,
|
117
|
+
args,
|
118
|
+
method_name: @method_name,
|
119
|
+
backtrace: orig_caller))
|
77
120
|
end
|
78
121
|
lookup(args, orig_caller).call(*args, &block)
|
79
122
|
rescue NotStubbedError
|
@@ -83,13 +126,29 @@ module MuchStub
|
|
83
126
|
|
84
127
|
def with(*args, &block)
|
85
128
|
orig_caller = caller_locations
|
86
|
-
unless arity_matches?(args)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
129
|
+
unless MuchStub.arity_matches?(@method, args)
|
130
|
+
raise(
|
131
|
+
StubArityError.new(
|
132
|
+
@method,
|
133
|
+
args,
|
134
|
+
method_name: @method_name,
|
135
|
+
backtrace: orig_caller))
|
91
136
|
end
|
92
137
|
@lookup[args] = block
|
138
|
+
self
|
139
|
+
end
|
140
|
+
|
141
|
+
def on_call(&on_call_block)
|
142
|
+
stub_block =
|
143
|
+
->(*args, &block) {
|
144
|
+
on_call_block.call(MuchStub::Call.new(*args, &block)) if on_call_block
|
145
|
+
}
|
146
|
+
if @lookup.empty?
|
147
|
+
@do = stub_block
|
148
|
+
elsif @lookup.has_value?(nil)
|
149
|
+
@lookup.transform_values!{ |value| value.nil? ? stub_block : value }
|
150
|
+
end
|
151
|
+
self
|
93
152
|
end
|
94
153
|
|
95
154
|
def teardown
|
@@ -100,7 +159,7 @@ module MuchStub
|
|
100
159
|
end
|
101
160
|
|
102
161
|
def inspect
|
103
|
-
"#<#{self.class}:#{
|
162
|
+
"#<#{self.class}:#{"0x0%x" % (object_id << 1)}" \
|
104
163
|
" @method_name=#{@method_name.inspect}" \
|
105
164
|
">"
|
106
165
|
end
|
@@ -137,7 +196,7 @@ module MuchStub
|
|
137
196
|
end
|
138
197
|
|
139
198
|
def lookup(args, orig_caller)
|
140
|
-
@lookup.fetch(args)
|
199
|
+
@lookup.fetch(args) {
|
141
200
|
self.do || begin
|
142
201
|
msg = "#{inspect_call(args)} not stubbed."
|
143
202
|
inspect_lookup_stubs.tap do |stubs|
|
@@ -145,13 +204,11 @@ module MuchStub
|
|
145
204
|
end
|
146
205
|
raise NotStubbedError, msg, orig_caller.map(&:to_s)
|
147
206
|
end
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
return true if @method.arity < 0 && args.size >= (@method.arity+1).abs # variable args
|
154
|
-
return false
|
207
|
+
} ||
|
208
|
+
raise(
|
209
|
+
StubError,
|
210
|
+
"#{inspect_call(args)} stubbed with no block.",
|
211
|
+
orig_caller.map(&:to_s))
|
155
212
|
end
|
156
213
|
|
157
214
|
def inspect_lookup_stubs
|
@@ -159,37 +216,47 @@ module MuchStub
|
|
159
216
|
end
|
160
217
|
|
161
218
|
def inspect_call(args)
|
162
|
-
"`#{@method_name}(#{args.map(&:inspect).join(
|
219
|
+
"`#{@method_name}(#{args.map(&:inspect).join(",")})`"
|
163
220
|
end
|
164
|
-
|
165
|
-
def number_of_args(arity)
|
166
|
-
if arity < 0
|
167
|
-
"at least #{(arity + 1).abs}"
|
168
|
-
else
|
169
|
-
arity
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
221
|
end
|
174
222
|
|
175
223
|
StubError = Class.new(ArgumentError)
|
176
224
|
NotStubbedError = Class.new(StubError)
|
177
|
-
StubArityError =
|
225
|
+
StubArityError =
|
226
|
+
Class.new(StubError) do
|
227
|
+
def initialize(method, args, method_name:, backtrace:)
|
228
|
+
msg = "arity mismatch on `#{method_name}`: " \
|
229
|
+
"expected #{number_of_args(method.arity)}, " \
|
230
|
+
"called with #{args.size}"
|
231
|
+
|
232
|
+
super(msg)
|
233
|
+
set_backtrace(Array(backtrace).map(&:to_s))
|
234
|
+
end
|
235
|
+
|
236
|
+
private
|
237
|
+
|
238
|
+
def number_of_args(arity)
|
239
|
+
if arity < 0
|
240
|
+
"at least #{(arity + 1).abs}"
|
241
|
+
else
|
242
|
+
arity
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
178
246
|
|
179
247
|
NullStub = Class.new do
|
180
248
|
def teardown; end # no-op
|
181
249
|
end
|
182
250
|
|
183
251
|
module ParameterList
|
184
|
-
|
185
|
-
LETTERS = ('a'..'z').to_a.freeze
|
252
|
+
LETTERS = ("a".."z").to_a.freeze
|
186
253
|
|
187
254
|
def self.new(object, method_name)
|
188
255
|
arity = get_arity(object, method_name)
|
189
256
|
params = build_params_from_arity(arity)
|
190
|
-
params <<
|
191
|
-
params <<
|
192
|
-
params.join(
|
257
|
+
params << "*args" if arity < 0
|
258
|
+
params << "&block"
|
259
|
+
params.join(", ")
|
193
260
|
end
|
194
261
|
|
195
262
|
private
|
@@ -210,9 +277,7 @@ module MuchStub
|
|
210
277
|
number_of_letters, letter_index = param_index.divmod(LETTERS.size)
|
211
278
|
LETTERS[letter_index] * number_of_letters
|
212
279
|
end
|
213
|
-
|
214
280
|
end
|
215
|
-
|
216
281
|
end
|
217
282
|
|
218
283
|
# Kernel#caller_locations polyfill for pre ruby 2.0.0
|