much-stub 0.1.0 → 0.1.5
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 +7 -7
- data/Gemfile +1 -1
- data/README.md +302 -35
- data/lib/much-stub.rb +116 -44
- data/lib/much-stub/call.rb +15 -0
- data/lib/much-stub/call_spy.rb +122 -0
- data/lib/much-stub/version.rb +1 -1
- data/much-stub.gemspec +4 -3
- data/test/helper.rb +3 -3
- data/test/support/factory.rb +1 -2
- data/test/system/much-stub_tests.rb +204 -223
- data/test/unit/call_spy_tests.rb +108 -0
- data/test/unit/call_tests.rb +68 -0
- data/test/unit/much-stub_tests.rb +193 -107
- metadata +41 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
5
|
-
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f4801d3a180d37bd01d02a52d1c524d38f432e4bb287a87d1e7753825efb180a
|
4
|
+
data.tar.gz: 422762b6602a2f05725fd61561e745bced7430b3e459ee7198a670ce86e922bd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3eb227fd853591c057e7869a6331cbd36ba0a308eb4a0a3a32313ea779ec740f89e32a297160fb40bd3400222af83693830c260df3c461d79d40e72d45d1df24
|
7
|
+
data.tar.gz: 8b7ab33cb886b60f4242bdaa6a1bcf9a5d1666c2b37a335eb01ab032d59841ff648b5bed5ffded02dfd40f2fe4451e1649ae45488a133c6773ef00670ae760d7
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -14,62 +14,329 @@ Note: this was originally implemented in and extracted from [Assert](https://git
|
|
14
14
|
## Usage
|
15
15
|
|
16
16
|
```ruby
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
# Given this object/API
|
18
|
+
|
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
|
20
27
|
end
|
21
|
-
|
28
|
+
my_object = my_class.new
|
22
29
|
|
23
|
-
|
24
|
-
# =>
|
25
|
-
|
30
|
+
my_object.my_method
|
31
|
+
# => "my-method"
|
32
|
+
my_object.my_value(123)
|
26
33
|
# => 123
|
27
|
-
|
34
|
+
my_object.my_value(456)
|
28
35
|
# => 456
|
29
36
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
+
# Create a new stub for the :my_method method
|
38
|
+
|
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)
|
37
46
|
# => StubError: arity mismatch
|
38
|
-
MuchStub.
|
47
|
+
MuchStub.(my_object, :my_method).with(123){ "stubbed-method" }
|
39
48
|
# => StubError: arity mismatch
|
40
|
-
MuchStub.stub_send(myobj, :mymeth) # call to the original method post-stub
|
41
|
-
# => 'meth'
|
42
49
|
|
43
|
-
|
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
|
56
|
+
|
57
|
+
MuchStub.(my_object, :my_value){ "stubbed-method" }
|
44
58
|
# => StubError: arity mismatch
|
45
|
-
MuchStub.
|
46
|
-
|
59
|
+
MuchStub.(my_object, :my_value).with(123){ |val| val.to_s }
|
60
|
+
my_object.my_value
|
47
61
|
# => StubError: arity mismatch
|
48
|
-
|
49
|
-
# =>
|
50
|
-
|
51
|
-
# => StubError: `
|
52
|
-
|
62
|
+
my_object.my_value(123)
|
63
|
+
# => "123"
|
64
|
+
my_object.my_value(456)
|
65
|
+
# => StubError: `my_value(456)` not stubbed.
|
66
|
+
|
67
|
+
# Call the original method after it has been stubbed.
|
68
|
+
|
69
|
+
MuchStub.stub_send(my_object, :my_value, 123)
|
53
70
|
# => 123
|
54
|
-
MuchStub.stub_send(
|
71
|
+
MuchStub.stub_send(my_object, :my_value, 456)
|
55
72
|
# => 456
|
56
73
|
|
57
|
-
|
58
|
-
|
74
|
+
# Unstub individual stubs
|
75
|
+
|
76
|
+
MuchStub.unstub(my_object, :my_method)
|
77
|
+
MuchStub.unstub(my_object, :my_value)
|
59
78
|
|
60
|
-
|
61
|
-
|
62
|
-
|
79
|
+
# OR blanket unstub all stubs
|
80
|
+
|
81
|
+
MuchStub.unstub!
|
82
|
+
|
83
|
+
# The original API/behavior is preserved after unstubbing
|
84
|
+
|
85
|
+
my_object.my_method
|
86
|
+
# => "my-method"
|
87
|
+
my_object.my_value(123)
|
63
88
|
# => 123
|
64
|
-
|
89
|
+
my_object.my_value(456)
|
65
90
|
# => 456
|
66
91
|
```
|
67
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
|
+
|
68
335
|
## Installation
|
69
336
|
|
70
337
|
Add this line to your application's Gemfile:
|
71
338
|
|
72
|
-
gem
|
339
|
+
gem "much-stub"
|
73
340
|
|
74
341
|
And then execute:
|
75
342
|
|
@@ -83,6 +350,6 @@ Or install it yourself as:
|
|
83
350
|
|
84
351
|
1. Fork it
|
85
352
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
86
|
-
3. Commit your changes (`git commit -am
|
353
|
+
3. Commit your changes (`git commit -am "Added some feature"`)
|
87
354
|
4. Push to the branch (`git push origin my-new-feature`)
|
88
355
|
5. Create new Pull Request
|
data/lib/much-stub.rb
CHANGED
@@ -1,19 +1,44 @@
|
|
1
1
|
require "much-stub/version"
|
2
2
|
|
3
|
-
|
3
|
+
require "much-stub/call"
|
4
|
+
require "much-stub/call_spy"
|
4
5
|
|
6
|
+
module MuchStub
|
5
7
|
def self.stubs
|
6
8
|
@stubs ||= {}
|
7
9
|
end
|
8
10
|
|
11
|
+
def self.stub_key(obj, meth)
|
12
|
+
MuchStub::Stub.key(obj, meth)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.arity_matches?(method, args)
|
16
|
+
return true if method.arity == args.size # mandatory args
|
17
|
+
return true if method.arity < 0 && args.size >= (method.arity+1).abs # variable args
|
18
|
+
return false
|
19
|
+
end
|
20
|
+
|
9
21
|
def self.stub(obj, meth, &block)
|
10
|
-
|
11
|
-
|
12
|
-
|
22
|
+
key = self.stub_key(obj, meth)
|
23
|
+
self.stubs[key] ||= MuchStub::Stub.new(obj, meth, caller_locations)
|
24
|
+
self.stubs[key].tap{ |s| s.do = block }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.call(*args, &block)
|
28
|
+
self.stub(*args, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.stub_on_call(*args, &on_call_block)
|
32
|
+
self.stub(*args).on_call(&on_call_block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.on_call(*args, &on_call_block)
|
36
|
+
self.stub_on_call(*args, &on_call_block)
|
13
37
|
end
|
14
38
|
|
15
39
|
def self.unstub(obj, meth)
|
16
|
-
|
40
|
+
key = self.stub_key(obj, meth)
|
41
|
+
(self.stubs.delete(key) || MuchStub::NullStub.new).teardown
|
17
42
|
end
|
18
43
|
|
19
44
|
def self.unstub!
|
@@ -28,8 +53,31 @@ module MuchStub
|
|
28
53
|
stub.call_method(args, &block)
|
29
54
|
end
|
30
55
|
|
31
|
-
|
56
|
+
def self.tap(obj, meth, &tap_block)
|
57
|
+
self.stub(obj, meth) { |*args, &block|
|
58
|
+
self.stub_send(obj, meth, *args, &block).tap { |value|
|
59
|
+
tap_block.call(value, *args, &block) if tap_block
|
60
|
+
}
|
61
|
+
}
|
62
|
+
end
|
32
63
|
|
64
|
+
def self.tap_on_call(obj, meth, &on_call_block)
|
65
|
+
self.tap(obj, meth) { |value, *args, &block|
|
66
|
+
on_call_block.call(value, MuchStub::Call.new(*args, &block)) if on_call_block
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.spy(obj, *meths, **return_values)
|
71
|
+
MuchStub::CallSpy.new(**return_values).call_spy_tap do |spy|
|
72
|
+
meths.each do |meth|
|
73
|
+
self.stub(obj, meth) { |*args, &block|
|
74
|
+
spy.__send__(meth, *args, &block)
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Stub
|
33
81
|
def self.key(object, method_name)
|
34
82
|
"--#{object.object_id}--#{method_name}--"
|
35
83
|
end
|
@@ -60,27 +108,45 @@ module MuchStub
|
|
60
108
|
|
61
109
|
def call(args, orig_caller = nil, &block)
|
62
110
|
orig_caller ||= caller_locations
|
63
|
-
unless arity_matches?(args)
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
111
|
+
unless MuchStub.arity_matches?(@method, args)
|
112
|
+
raise(
|
113
|
+
StubArityError.new(
|
114
|
+
@method,
|
115
|
+
args,
|
116
|
+
method_name: @method_name,
|
117
|
+
backtrace: orig_caller))
|
68
118
|
end
|
69
119
|
lookup(args, orig_caller).call(*args, &block)
|
70
|
-
rescue NotStubbedError
|
120
|
+
rescue NotStubbedError
|
71
121
|
@lookup.rehash
|
72
122
|
lookup(args, orig_caller).call(*args, &block)
|
73
123
|
end
|
74
124
|
|
75
125
|
def with(*args, &block)
|
76
126
|
orig_caller = caller_locations
|
77
|
-
unless arity_matches?(args)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
127
|
+
unless MuchStub.arity_matches?(@method, args)
|
128
|
+
raise(
|
129
|
+
StubArityError.new(
|
130
|
+
@method,
|
131
|
+
args,
|
132
|
+
method_name: @method_name,
|
133
|
+
backtrace: orig_caller))
|
82
134
|
end
|
83
135
|
@lookup[args] = block
|
136
|
+
self
|
137
|
+
end
|
138
|
+
|
139
|
+
def on_call(&on_call_block)
|
140
|
+
stub_block =
|
141
|
+
->(*args, &block) {
|
142
|
+
on_call_block.call(MuchStub::Call.new(*args, &block)) if on_call_block
|
143
|
+
}
|
144
|
+
if @lookup.empty?
|
145
|
+
@do = stub_block
|
146
|
+
elsif @lookup.has_value?(nil)
|
147
|
+
@lookup.transform_values!{ |value| value.nil? ? stub_block : value }
|
148
|
+
end
|
149
|
+
self
|
84
150
|
end
|
85
151
|
|
86
152
|
def teardown
|
@@ -91,7 +157,7 @@ module MuchStub
|
|
91
157
|
end
|
92
158
|
|
93
159
|
def inspect
|
94
|
-
"#<#{self.class}:#{
|
160
|
+
"#<#{self.class}:#{"0x0%x" % (object_id << 1)}" \
|
95
161
|
" @method_name=#{@method_name.inspect}" \
|
96
162
|
">"
|
97
163
|
end
|
@@ -128,7 +194,7 @@ module MuchStub
|
|
128
194
|
end
|
129
195
|
|
130
196
|
def lookup(args, orig_caller)
|
131
|
-
@lookup.fetch(args)
|
197
|
+
@lookup.fetch(args) {
|
132
198
|
self.do || begin
|
133
199
|
msg = "#{inspect_call(args)} not stubbed."
|
134
200
|
inspect_lookup_stubs.tap do |stubs|
|
@@ -136,13 +202,11 @@ module MuchStub
|
|
136
202
|
end
|
137
203
|
raise NotStubbedError, msg, orig_caller.map(&:to_s)
|
138
204
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
return true if @method.arity < 0 && args.size >= (@method.arity+1).abs # variable args
|
145
|
-
return false
|
205
|
+
} ||
|
206
|
+
raise(
|
207
|
+
StubError,
|
208
|
+
"#{inspect_call(args)} stubbed with no block.",
|
209
|
+
orig_caller.map(&:to_s))
|
146
210
|
end
|
147
211
|
|
148
212
|
def inspect_lookup_stubs
|
@@ -150,37 +214,47 @@ module MuchStub
|
|
150
214
|
end
|
151
215
|
|
152
216
|
def inspect_call(args)
|
153
|
-
"`#{@method_name}(#{args.map(&:inspect).join(
|
217
|
+
"`#{@method_name}(#{args.map(&:inspect).join(",")})`"
|
154
218
|
end
|
155
|
-
|
156
|
-
def number_of_args(arity)
|
157
|
-
if arity < 0
|
158
|
-
"at least #{(arity + 1).abs}"
|
159
|
-
else
|
160
|
-
arity
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
219
|
end
|
165
220
|
|
166
221
|
StubError = Class.new(ArgumentError)
|
167
222
|
NotStubbedError = Class.new(StubError)
|
168
|
-
StubArityError =
|
223
|
+
StubArityError =
|
224
|
+
Class.new(StubError) do
|
225
|
+
def initialize(method, args, method_name:, backtrace:)
|
226
|
+
msg = "arity mismatch on `#{method_name}`: " \
|
227
|
+
"expected #{number_of_args(method.arity)}, " \
|
228
|
+
"called with #{args.size}"
|
229
|
+
|
230
|
+
super(msg)
|
231
|
+
set_backtrace(Array(backtrace).map(&:to_s))
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def number_of_args(arity)
|
237
|
+
if arity < 0
|
238
|
+
"at least #{(arity + 1).abs}"
|
239
|
+
else
|
240
|
+
arity
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
169
244
|
|
170
245
|
NullStub = Class.new do
|
171
246
|
def teardown; end # no-op
|
172
247
|
end
|
173
248
|
|
174
249
|
module ParameterList
|
175
|
-
|
176
|
-
LETTERS = ('a'..'z').to_a.freeze
|
250
|
+
LETTERS = ("a".."z").to_a.freeze
|
177
251
|
|
178
252
|
def self.new(object, method_name)
|
179
253
|
arity = get_arity(object, method_name)
|
180
254
|
params = build_params_from_arity(arity)
|
181
|
-
params <<
|
182
|
-
params <<
|
183
|
-
params.join(
|
255
|
+
params << "*args" if arity < 0
|
256
|
+
params << "&block"
|
257
|
+
params.join(", ")
|
184
258
|
end
|
185
259
|
|
186
260
|
private
|
@@ -201,9 +275,7 @@ module MuchStub
|
|
201
275
|
number_of_letters, letter_index = param_index.divmod(LETTERS.size)
|
202
276
|
LETTERS[letter_index] * number_of_letters
|
203
277
|
end
|
204
|
-
|
205
278
|
end
|
206
|
-
|
207
279
|
end
|
208
280
|
|
209
281
|
# Kernel#caller_locations polyfill for pre ruby 2.0.0
|