much-stub 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,447 @@
1
+ require 'assert'
2
+ require 'much-stub'
3
+
4
+ require 'test/support/factory'
5
+
6
+ module MuchStub
7
+
8
+ class UnitTests < Assert::Context
9
+ desc "MuchStub"
10
+ end
11
+
12
+ class ApiTests < UnitTests
13
+ desc "api"
14
+ setup do
15
+ @orig_value = Factory.string
16
+ @stub_value = Factory.string
17
+
18
+ @myclass = Class.new do
19
+ def initialize(value); @value = value; end
20
+ def mymeth; @value; end
21
+ end
22
+ @myobj = @myclass.new(@orig_value)
23
+ end
24
+
25
+ should "build a stub" do
26
+ stub1 = MuchStub.stub(@myobj, :mymeth)
27
+ assert_kind_of MuchStub::Stub, stub1
28
+ end
29
+
30
+ should "lookup stubs that have been called before" do
31
+ stub1 = MuchStub.stub(@myobj, :mymeth)
32
+ stub2 = MuchStub.stub(@myobj, :mymeth)
33
+ assert_same stub1, stub2
34
+ end
35
+
36
+ should "set the stub's do block if given a block" do
37
+ MuchStub.stub(@myobj, :mymeth)
38
+ assert_raises(MuchStub::NotStubbedError){ @myobj.mymeth }
39
+ MuchStub.stub(@myobj, :mymeth){ @stub_value }
40
+ assert_equal @stub_value, @myobj.mymeth
41
+ end
42
+
43
+ should "teardown stubs" do
44
+ assert_equal @orig_value, @myobj.mymeth
45
+ MuchStub.unstub(@myobj, :mymeth)
46
+ assert_equal @orig_value, @myobj.mymeth
47
+
48
+ assert_equal @orig_value, @myobj.mymeth
49
+ MuchStub.stub(@myobj, :mymeth){ @stub_value }
50
+ assert_equal @stub_value, @myobj.mymeth
51
+ MuchStub.unstub(@myobj, :mymeth)
52
+ assert_equal @orig_value, @myobj.mymeth
53
+ end
54
+
55
+ should "know and teardown all stubs" do
56
+ assert_equal @orig_value, @myobj.mymeth
57
+
58
+ MuchStub.stub(@myobj, :mymeth){ @stub_value }
59
+ assert_equal @stub_value, @myobj.mymeth
60
+ assert_equal 1, MuchStub.stubs.size
61
+
62
+ MuchStub.unstub!
63
+ assert_equal @orig_value, @myobj.mymeth
64
+ assert_empty MuchStub.stubs
65
+ end
66
+
67
+ should "be able to call a stub's original method" do
68
+ err = assert_raises(NotStubbedError){ MuchStub.stub_send(@myobj, :mymeth) }
69
+ assert_includes 'not stubbed.', err.message
70
+ assert_includes 'test/unit/much-stub_tests.rb', err.backtrace.first
71
+
72
+ MuchStub.stub(@myobj, :mymeth){ @stub_value }
73
+
74
+ assert_equal @stub_value, @myobj.mymeth
75
+ assert_equal @orig_value, MuchStub.stub_send(@myobj, :mymeth)
76
+ end
77
+
78
+ end
79
+
80
+ class StubTests < UnitTests
81
+ desc "Stub"
82
+ setup do
83
+ @myclass = Class.new do
84
+ def mymeth; 'meth'; end
85
+ def myval(val); val; end
86
+ def myargs(*args); args; end
87
+ def myvalargs(val1, val2, *args); [val1, val2, args]; end
88
+ def myblk(&block); block.call; end
89
+ end
90
+ @myobj = @myclass.new
91
+
92
+ @stub = MuchStub::Stub.new(@myobj, :mymeth)
93
+ end
94
+ subject{ @stub }
95
+
96
+ should have_readers :method_name, :name, :ivar_name, :do
97
+ should have_writers :do
98
+ should have_cmeths :key
99
+ should have_imeths :call_method, :call, :with, :teardown
100
+
101
+ should "generate a key given an object and method name" do
102
+ obj = @myobj
103
+ meth = :mymeth
104
+ assert_equal "--#{obj.object_id}--#{meth}--", MuchStub::Stub.key(obj, meth)
105
+ end
106
+
107
+ should "know its names" do
108
+ assert_equal 'mymeth', subject.method_name
109
+ expected = "__muchstub_stub__#{@myobj.object_id}_#{subject.method_name}"
110
+ assert_equal expected, subject.name
111
+ expected = "@__muchstub_stub_#{@myobj.object_id}_" \
112
+ "#{subject.method_name.to_sym.object_id}"
113
+ assert_equal expected, subject.ivar_name
114
+ end
115
+
116
+ should "complain when called if no do block was given" do
117
+ err = assert_raises(MuchStub::NotStubbedError){ @myobj.mymeth }
118
+ assert_includes 'not stubbed.', err.message
119
+ assert_includes 'test/unit/much-stub_tests.rb', err.backtrace.first
120
+
121
+ subject.do = proc{ 'mymeth' }
122
+ assert_nothing_raised do
123
+ @myobj.mymeth
124
+ end
125
+
126
+ assert_nothing_raised do
127
+ MuchStub::Stub.new(@myobj, :mymeth){ 'mymeth' }
128
+ end
129
+ end
130
+
131
+ should "complain if stubbing a method that the object doesn't respond to" do
132
+ err = assert_raises(MuchStub::StubError){ MuchStub::Stub.new(@myobj, :some_other_meth) }
133
+ assert_includes 'does not respond to', err.message
134
+ assert_includes 'test/unit/much-stub_tests.rb', err.backtrace.first
135
+ end
136
+
137
+ should "complain if stubbed and called with no `do` proc given" do
138
+ assert_raises(MuchStub::NotStubbedError){ @myobj.mymeth }
139
+ end
140
+
141
+ should "complain if stubbed and called with mismatched arity" do
142
+ MuchStub::Stub.new(@myobj, :myval){ 'myval' }
143
+ err = assert_raises(MuchStub::StubArityError){ @myobj.myval }
144
+ assert_includes 'arity mismatch on', err.message
145
+ assert_includes 'test/unit/much-stub_tests.rb', err.backtrace.first
146
+
147
+ assert_nothing_raised { @myobj.myval(1) }
148
+ assert_raises(MuchStub::StubArityError){ @myobj.myval(1,2) }
149
+
150
+ MuchStub::Stub.new(@myobj, :myargs){ 'myargs' }
151
+ assert_nothing_raised { @myobj.myargs }
152
+ assert_nothing_raised { @myobj.myargs(1) }
153
+ assert_nothing_raised { @myobj.myargs(1,2) }
154
+
155
+ MuchStub::Stub.new(@myobj, :myvalargs){ 'myvalargs' }
156
+ assert_raises(MuchStub::StubArityError){ @myobj.myvalargs }
157
+ assert_raises(MuchStub::StubArityError){ @myobj.myvalargs(1) }
158
+ assert_nothing_raised { @myobj.myvalargs(1,2) }
159
+ assert_nothing_raised { @myobj.myvalargs(1,2,3) }
160
+ end
161
+
162
+ should "complain if stubbed with mismatched arity" do
163
+ err = assert_raises(MuchStub::StubArityError) do
164
+ MuchStub::Stub.new(@myobj, :myval).with(){ 'myval' }
165
+ end
166
+ assert_includes 'arity mismatch on', err.message
167
+ assert_includes 'test/unit/much-stub_tests.rb', err.backtrace.first
168
+
169
+ assert_raises(MuchStub::StubArityError) do
170
+ MuchStub::Stub.new(@myobj, :myval).with(1,2){ 'myval' }
171
+ end
172
+ assert_nothing_raised do
173
+ MuchStub::Stub.new(@myobj, :myval).with(1){ 'myval' }
174
+ end
175
+
176
+ assert_nothing_raised do
177
+ MuchStub::Stub.new(@myobj, :myargs).with(){ 'myargs' }
178
+ end
179
+ assert_nothing_raised do
180
+ MuchStub::Stub.new(@myobj, :myargs).with(1,2){ 'myargs' }
181
+ end
182
+ assert_nothing_raised do
183
+ MuchStub::Stub.new(@myobj, :myargs).with(1){ 'myargs' }
184
+ end
185
+
186
+ assert_raises(MuchStub::StubArityError) do
187
+ MuchStub::Stub.new(@myobj, :myvalargs).with(){ 'myvalargs' }
188
+ end
189
+ assert_raises(MuchStub::StubArityError) do
190
+ MuchStub::Stub.new(@myobj, :myvalargs).with(1){ 'myvalargs' }
191
+ end
192
+ assert_nothing_raised do
193
+ MuchStub::Stub.new(@myobj, :myvalargs).with(1,2){ 'myvalargs' }
194
+ end
195
+ assert_nothing_raised do
196
+ MuchStub::Stub.new(@myobj, :myvalargs).with(1,2,3){ 'myvalargs' }
197
+ end
198
+ end
199
+
200
+ should "stub methods with no args" do
201
+ subject.teardown
202
+
203
+ assert_equal 'meth', @myobj.mymeth
204
+ MuchStub::Stub.new(@myobj, :mymeth){ 'mymeth' }
205
+ assert_equal 'mymeth', @myobj.mymeth
206
+ end
207
+
208
+ should "stub methods with required arg" do
209
+ assert_equal 1, @myobj.myval(1)
210
+ stub = MuchStub::Stub.new(@myobj, :myval){ |val| val.to_s }
211
+ assert_equal '1', @myobj.myval(1)
212
+ assert_equal '2', @myobj.myval(2)
213
+ stub.with(2){ 'two' }
214
+ assert_equal 'two', @myobj.myval(2)
215
+ end
216
+
217
+ should "stub methods with variable args" do
218
+ assert_equal [1,2], @myobj.myargs(1,2)
219
+ stub = MuchStub::Stub.new(@myobj, :myargs){ |*args| args.join(',') }
220
+ assert_equal '1,2', @myobj.myargs(1,2)
221
+ assert_equal '3,4,5', @myobj.myargs(3,4,5)
222
+ stub.with(3,4,5){ 'three-four-five' }
223
+ assert_equal 'three-four-five', @myobj.myargs(3,4,5)
224
+ end
225
+
226
+ should "stub methods with required args and variable args" do
227
+ assert_equal [1,2, [3]], @myobj.myvalargs(1,2,3)
228
+ stub = MuchStub::Stub.new(@myobj, :myvalargs){ |*args| args.join(',') }
229
+ assert_equal '1,2,3', @myobj.myvalargs(1,2,3)
230
+ assert_equal '3,4,5', @myobj.myvalargs(3,4,5)
231
+ stub.with(3,4,5){ 'three-four-five' }
232
+ assert_equal 'three-four-five', @myobj.myvalargs(3,4,5)
233
+ end
234
+
235
+ should "stub methods that yield blocks" do
236
+ blkcalled = false
237
+ blk = proc{ blkcalled = true }
238
+ @myobj.myblk(&blk)
239
+ assert_equal true, blkcalled
240
+
241
+ blkcalled = false
242
+ MuchStub::Stub.new(@myobj, :myblk){ blkcalled = 'true' }
243
+ @myobj.myblk(&blk)
244
+ assert_equal 'true', blkcalled
245
+ end
246
+
247
+ should "stub methods even if they are not local to the object" do
248
+ mydelegatorclass = Class.new do
249
+ def initialize(delegateclass)
250
+ @delegate = delegateclass.new
251
+ end
252
+ def respond_to?(meth)
253
+ @delegate.respond_to?(meth) || super
254
+ end
255
+ def method_missing(meth, *args, &block)
256
+ respond_to?(meth) ? @delegate.send(meth, *args, &block) : super
257
+ end
258
+ end
259
+ mydelegator = mydelegatorclass.new(@myclass)
260
+
261
+ assert_equal [1,2,[3]], mydelegator.myvalargs(1,2,3)
262
+ stub = MuchStub::Stub.new(mydelegator, :myvalargs){ |*args| args.inspect }
263
+ assert_equal '[1, 2, 3]', mydelegator.myvalargs(1,2,3)
264
+ assert_equal '[4, 5, 6]', mydelegator.myvalargs(4,5,6)
265
+ stub.with(4,5,6){ 'four-five-six' }
266
+ assert_equal 'four-five-six', mydelegator.myvalargs(4,5,6)
267
+ end
268
+
269
+ should "call to the original method" do
270
+ subject.teardown
271
+
272
+ # no args
273
+ stub = MuchStub::Stub.new(@myobj, :mymeth){ 'mymeth' }
274
+ assert_equal 'mymeth', stub.call([])
275
+ assert_equal 'meth', stub.call_method([])
276
+
277
+ # static args
278
+ stub = MuchStub::Stub.new(@myobj, :myval){ |val| val.to_s }
279
+ assert_equal '1', stub.call([1])
280
+ assert_equal 1, stub.call_method([1])
281
+ assert_equal '2', stub.call([2])
282
+ assert_equal 2, stub.call_method([2])
283
+ stub.with(2){ 'two' }
284
+ assert_equal 'two', stub.call([2])
285
+ assert_equal 2, stub.call_method([2])
286
+
287
+ # dynamic args
288
+ stub = MuchStub::Stub.new(@myobj, :myargs){ |*args| args.join(',') }
289
+ assert_equal '1,2', stub.call([1,2])
290
+ assert_equal [1,2], stub.call_method([1,2])
291
+ assert_equal '3,4,5', stub.call([3,4,5])
292
+ assert_equal [3,4,5], stub.call_method([3,4,5])
293
+ stub.with(3,4,5){ 'three-four-five' }
294
+ assert_equal 'three-four-five', stub.call([3,4,5])
295
+ assert_equal [3,4,5], stub.call_method([3,4,5])
296
+
297
+ # mixed static/dynamic args
298
+ stub = MuchStub::Stub.new(@myobj, :myvalargs){ |*args| args.join(',') }
299
+ assert_equal '1,2,3', stub.call([1,2,3])
300
+ assert_equal [1,2, [3]], stub.call_method([1,2,3])
301
+ assert_equal '3,4,5', stub.call([3,4,5])
302
+ assert_equal [3,4,[5]], stub.call_method([3,4,5])
303
+ stub.with(3,4,5){ 'three-four-five' }
304
+ assert_equal 'three-four-five', stub.call([3,4,5])
305
+ assert_equal [3,4,[5]], stub.call_method([3,4,5])
306
+
307
+ # blocks
308
+ blkcalled = false
309
+ blk = proc{ blkcalled = true }
310
+ stub = MuchStub::Stub.new(@myobj, :myblk){ blkcalled = 'true' }
311
+ stub.call([], &blk)
312
+ assert_equal 'true', blkcalled
313
+ stub.call_method([], &blk)
314
+ assert_equal true, blkcalled
315
+ end
316
+
317
+ should "store and remove itself on asserts main module" do
318
+ assert_equal subject, MuchStub.instance_variable_get(subject.ivar_name)
319
+ subject.teardown
320
+ assert_nil MuchStub.instance_variable_get(subject.ivar_name)
321
+ end
322
+
323
+ should "be removable" do
324
+ assert_equal 1, @myobj.myval(1)
325
+ stub = MuchStub::Stub.new(@myobj, :myval){ |val| val.to_s }
326
+ assert_equal '1', @myobj.myval(1)
327
+ stub.teardown
328
+ assert_equal 1, @myobj.myval(1)
329
+ end
330
+
331
+ should "have a readable inspect" do
332
+ expected = "#<#{subject.class}:#{'0x0%x' % (subject.object_id << 1)}" \
333
+ " @method_name=#{subject.method_name.inspect}>"
334
+ assert_equal expected, subject.inspect
335
+ end
336
+
337
+ end
338
+
339
+ class MutatingArgsStubTests < StubTests
340
+ desc "with args that are mutated after they've been used to stub"
341
+ setup do
342
+ @arg = ChangeHashObject.new
343
+
344
+ @stub = MuchStub::Stub.new(@myobj, :myval)
345
+ @stub.with(@arg){ true }
346
+
347
+ @arg.change!
348
+ end
349
+ subject{ @stub }
350
+
351
+ should "not raise a stub error when called" do
352
+ assert_nothing_raised{ @stub.call([@arg]) }
353
+ end
354
+
355
+ end
356
+
357
+ class StubInheritedMethodAfterStubbedOnParentTests < StubTests
358
+ desc "stubbing an inherited method after its stubbed on the parent class"
359
+ setup do
360
+ @parent_class = Class.new
361
+ @child_class = Class.new(@parent_class)
362
+
363
+ @parent_stub = MuchStub::Stub.new(@parent_class, :new)
364
+ @child_stub = MuchStub::Stub.new(@child_class, :new)
365
+ end
366
+
367
+ should "allow stubbing them independently of each other" do
368
+ assert_nothing_raised do
369
+ @parent_stub.with(1, 2){ 'parent' }
370
+ @child_stub.with(1, 2){ 'child' }
371
+ end
372
+ assert_equal 'parent', @parent_class.new(1, 2)
373
+ assert_equal 'child', @child_class.new(1, 2)
374
+ end
375
+
376
+ should "not raise any errors when tearing down the parent before the child" do
377
+ assert_nothing_raised do
378
+ @parent_stub.teardown
379
+ @child_stub.teardown
380
+ end
381
+ end
382
+
383
+ end
384
+
385
+ class NullStubTests < UnitTests
386
+ desc "NullStub"
387
+ setup do
388
+ @ns = NullStub.new
389
+ end
390
+ subject{ @ns }
391
+
392
+ should have_imeths :teardown
393
+
394
+ end
395
+
396
+ class ParameterListTests < UnitTests
397
+ desc "ParameterList"
398
+ setup do
399
+ many_args = (0..ParameterList::LETTERS.size).map do
400
+ Assert::Factory.string
401
+ end.join(', ')
402
+ @object = Class.new
403
+ # use `class_eval` with string to easily define methods with many params
404
+ @object.class_eval <<-methods
405
+ def self.noargs; end
406
+ def self.anyargs(*args); end
407
+ def self.manyargs(#{many_args}); end
408
+ def self.minargs(#{many_args}, *args); end
409
+ methods
410
+ end
411
+ subject{ ParameterList }
412
+
413
+ should "build a parameter list for a method that takes no args" do
414
+ assert_equal '&block', subject.new(@object, 'noargs')
415
+ end
416
+
417
+ should "build a parameter list for a method that takes any args" do
418
+ assert_equal '*args, &block', subject.new(@object, 'anyargs')
419
+ end
420
+
421
+ should "build a parameter list for a method that takes many args" do
422
+ expected = "#{ParameterList::LETTERS.join(', ')}, aa, &block"
423
+ assert_equal expected, subject.new(@object, 'manyargs')
424
+ end
425
+
426
+ should "build a parameter list for a method that takes a minimum number of args" do
427
+ expected = "#{ParameterList::LETTERS.join(', ')}, aa, *args, &block"
428
+ assert_equal expected, subject.new(@object, 'minargs')
429
+ end
430
+
431
+ end
432
+
433
+ class ChangeHashObject
434
+ def initialize
435
+ @value = nil
436
+ end
437
+
438
+ def hash
439
+ @value.hash
440
+ end
441
+
442
+ def change!
443
+ @value = 1
444
+ end
445
+ end
446
+
447
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: much-stub
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kelly Redding
8
+ - Collin Redding
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2018-06-05 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: assert
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: 2.16.3
23
+ type: :development
24
+ version_requirements: *id001
25
+ description: Stubbing API for replacing method calls on objects in test runs.
26
+ email:
27
+ - kelly@kellyredding.com
28
+ - collin.redding@me.com
29
+ executables: []
30
+
31
+ extensions: []
32
+
33
+ extra_rdoc_files: []
34
+
35
+ files:
36
+ - .gitignore
37
+ - Gemfile
38
+ - LICENSE
39
+ - README.md
40
+ - lib/much-stub.rb
41
+ - lib/much-stub/version.rb
42
+ - log/.gitkeep
43
+ - much-stub.gemspec
44
+ - test/helper.rb
45
+ - test/support/factory.rb
46
+ - test/system/much-stub_tests.rb
47
+ - test/unit/much-stub_tests.rb
48
+ - tmp/.gitkeep
49
+ homepage: https://github.com/redding/much-stub
50
+ licenses:
51
+ - MIT
52
+ metadata: {}
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - &id002
62
+ - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - *id002
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 2.6.6
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: Stubbing API for replacing method calls on objects in test runs.
75
+ test_files:
76
+ - test/helper.rb
77
+ - test/support/factory.rb
78
+ - test/system/much-stub_tests.rb
79
+ - test/unit/much-stub_tests.rb