spy 0.1.0 → 0.2.1

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.
Files changed (42) hide show
  1. data/.travis.yml +5 -0
  2. data/Gemfile +1 -0
  3. data/README.md +22 -7
  4. data/Rakefile +2 -0
  5. data/lib/spy.rb +39 -6
  6. data/lib/spy/agency.rb +42 -27
  7. data/lib/spy/call_log.rb +26 -0
  8. data/lib/spy/constant.rb +72 -14
  9. data/lib/spy/core_ext/marshal.rb +1 -0
  10. data/lib/spy/double.rb +17 -0
  11. data/lib/spy/nest.rb +27 -0
  12. data/lib/spy/subroutine.rb +146 -44
  13. data/lib/spy/version.rb +1 -1
  14. data/spec/spy/any_instance_spec.rb +518 -0
  15. data/spec/spy/mock_spec.rb +46 -554
  16. data/spec/spy/mutate_const_spec.rb +21 -63
  17. data/spec/spy/null_object_mock_spec.rb +11 -39
  18. data/spec/spy/partial_mock_spec.rb +3 -62
  19. data/spec/spy/stash_spec.rb +30 -37
  20. data/spec/spy/stub_spec.rb +0 -6
  21. data/spec/spy/to_ary_spec.rb +5 -5
  22. data/test/integration/test_constant_spying.rb +1 -1
  23. data/test/integration/test_instance_method.rb +32 -0
  24. data/test/integration/test_subroutine_spying.rb +7 -4
  25. data/test/spy/test_double.rb +4 -0
  26. data/test/spy/test_subroutine.rb +28 -3
  27. data/test/support/pen.rb +15 -0
  28. metadata +8 -30
  29. data/spec/spy/bug_report_10260_spec.rb +0 -8
  30. data/spec/spy/bug_report_10263_spec.rb +0 -24
  31. data/spec/spy/bug_report_496_spec.rb +0 -18
  32. data/spec/spy/bug_report_600_spec.rb +0 -24
  33. data/spec/spy/bug_report_7611_spec.rb +0 -16
  34. data/spec/spy/bug_report_8165_spec.rb +0 -31
  35. data/spec/spy/bug_report_830_spec.rb +0 -21
  36. data/spec/spy/bug_report_957_spec.rb +0 -22
  37. data/spec/spy/double_spec.rb +0 -12
  38. data/spec/spy/failing_argument_matchers_spec.rb +0 -94
  39. data/spec/spy/options_hash_spec.rb +0 -35
  40. data/spec/spy/precise_counts_spec.rb +0 -68
  41. data/spec/spy/stubbed_message_expectations_spec.rb +0 -47
  42. data/spec/spy/test_double_spec.rb +0 -54
@@ -29,8 +29,12 @@ module Spy
29
29
  end
30
30
 
31
31
  def on_const(const_name)
32
- parent_const = recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
33
- Spy.on_const(parent_const, const_name.split('::').last.to_sym)
32
+ if const_name.include? "::"
33
+ args = [recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, '')), const_name.split('::').last.to_sym]
34
+ else
35
+ args = [const_name.to_sym]
36
+ end
37
+ Spy.get_const(*args) || Spy.on_const(*args)
34
38
  end
35
39
 
36
40
  def stub_const(const_name, value)
@@ -172,17 +176,9 @@ module Spy
172
176
  it_behaves_like "unloaded constant hiding", "TestClass::Hash"
173
177
 
174
178
  it 'does not hide the top-level constant' do
175
- top_level_hash = ::Hash
176
-
177
- hide_const("TestClass::Hash")
178
- expect(::Hash).to equal(top_level_hash)
179
- end
180
-
181
- it 'does not affect the ability to access the top-level constant from nested contexts', :silence_warnings do
182
- top_level_hash = ::Hash
183
-
184
- hide_const("TestClass::Hash")
185
- expect(TestClass::Hash).to equal(top_level_hash)
179
+ expect {
180
+ hide_const("TestClass::Hash")
181
+ }.to raise_error
186
182
  end
187
183
  end
188
184
 
@@ -318,14 +314,6 @@ module Spy
318
314
  it_behaves_like "loaded constant stubbing", "TestClass::Nested"
319
315
  end
320
316
 
321
- context 'for a loaded constant prefixed with ::' do
322
- it_behaves_like 'loaded constant stubbing', "::TestClass"
323
- end
324
-
325
- context 'for an unloaded constant prefixed with ::' do
326
- it_behaves_like 'unloaded constant stubbing', "::SomeUndefinedConst"
327
- end
328
-
329
317
  context 'for an unloaded constant with nested name that matches a top-level constant' do
330
318
  it_behaves_like "unloaded constant stubbing", "TestClass::Hash"
331
319
  end
@@ -338,28 +326,6 @@ module Spy
338
326
  it_behaves_like "unloaded constant stubbing", "X"
339
327
  end
340
328
 
341
- context 'for an unloaded nested constant' do
342
- it_behaves_like "unloaded constant stubbing", "X::Y"
343
-
344
- it 'removes the root constant when rspec clears its mocks' do
345
- expect(defined?(X)).to be_false
346
- stub_const("X::Y", 7)
347
- reset_rspec_mocks
348
- expect(defined?(X)).to be_false
349
- end
350
- end
351
-
352
- context 'for an unloaded deeply nested constant' do
353
- it_behaves_like "unloaded constant stubbing", "X::Y::Z"
354
-
355
- it 'removes the root constant when rspec clears its mocks' do
356
- expect(defined?(X)).to be_false
357
- stub_const("X::Y::Z", 7)
358
- reset_rspec_mocks
359
- expect(defined?(X)).to be_false
360
- end
361
- end
362
-
363
329
  context 'for an unloaded constant nested within a loaded constant' do
364
330
  it_behaves_like "unloaded constant stubbing", "TestClass::X"
365
331
 
@@ -402,8 +368,12 @@ module Spy
402
368
  end
403
369
 
404
370
  def on_const(const_name)
405
- parent_const = recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
406
- Spy.on_const(parent_const, const_name.split('::').last.to_sym)
371
+ if const_name.include? "::"
372
+ args = [recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, '')), const_name.split('::').last.to_sym]
373
+ else
374
+ args = [const_name.to_sym]
375
+ end
376
+ Spy.get_const(*args) || Spy.on_const(*args)
407
377
  end
408
378
 
409
379
  def stub_const(const_name, value)
@@ -415,8 +385,12 @@ module Spy
415
385
  end
416
386
 
417
387
  def original(const_name)
418
- parent_const = recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
419
- Spy.get_const(parent_const, const_name.split('::').last.to_sym)
388
+ if const_name.include? "::"
389
+ args = [recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, '')), const_name.split('::').last.to_sym]
390
+ else
391
+ args = [const_name.to_sym]
392
+ end
393
+ Spy.get_const(*args)
420
394
  end
421
395
 
422
396
  describe ".original" do
@@ -425,8 +399,6 @@ module Spy
425
399
 
426
400
  it("exposes its name") { expect(const.name).to eq("TestClass::M") }
427
401
  it("indicates it was previously defined") { expect(const).to be_previously_defined }
428
- it("indicates it has not been mutated") { expect(const).not_to be_mutated }
429
- it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
430
402
  it("indicates it has not been hidden") { expect(const).not_to be_hidden }
431
403
  it("exposes its original value") { expect(const.original_value).to eq(:m) }
432
404
  end
@@ -437,8 +409,6 @@ module Spy
437
409
 
438
410
  it("exposes its name") { expect(const.name).to eq("TestClass::M") }
439
411
  it("indicates it was previously defined") { expect(const).to be_previously_defined }
440
- it("indicates it has been mutated") { expect(const).to be_mutated }
441
- it("indicates it has been stubbed") { expect(const).to be_stubbed }
442
412
  it("indicates it has not been hidden") { expect(const).not_to be_hidden }
443
413
  it("exposes its original value") { expect(const.original_value).to eq(:m) }
444
414
  end
@@ -449,8 +419,6 @@ module Spy
449
419
 
450
420
  it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
451
421
  it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
452
- it("indicates it has been mutated") { expect(const).to be_mutated }
453
- it("indicates it has been stubbed") { expect(const).to be_stubbed }
454
422
  it("indicates it has not been hidden") { expect(const).not_to be_hidden }
455
423
  it("returns nil for the original value") { expect(const.original_value).to be_nil }
456
424
  end
@@ -460,8 +428,6 @@ module Spy
460
428
 
461
429
  it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
462
430
  it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
463
- it("indicates it has not been mutated") { expect(const).not_to be_mutated }
464
- it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
465
431
  it("indicates it has not been hidden") { expect(const).not_to be_hidden }
466
432
  it("returns nil for the original value") { expect(const.original_value).to be_nil }
467
433
  end
@@ -473,8 +439,6 @@ module Spy
473
439
 
474
440
  it("exposes its name") { expect(const.name).to eq("TestClass::M") }
475
441
  it("indicates it was previously defined") { expect(const).to be_previously_defined }
476
- it("indicates it has been mutated") { expect(const).to be_mutated }
477
- it("indicates it has been stubbed") { expect(const).to be_stubbed }
478
442
  it("indicates it has not been hidden") { expect(const).not_to be_hidden }
479
443
  it("exposes its original value") { expect(const.original_value).to eq(:m) }
480
444
  end
@@ -486,8 +450,6 @@ module Spy
486
450
 
487
451
  it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
488
452
  it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
489
- it("indicates it has been mutated") { expect(const).to be_mutated }
490
- it("indicates it has been stubbed") { expect(const).to be_stubbed }
491
453
  it("indicates it has not been hidden") { expect(const).not_to be_hidden }
492
454
  it("returns nil for the original value") { expect(const.original_value).to be_nil }
493
455
  end
@@ -498,8 +460,6 @@ module Spy
498
460
 
499
461
  it("exposes its name") { expect(const.name).to eq("TestClass::M") }
500
462
  it("indicates it was previously defined") { expect(const).to be_previously_defined }
501
- it("indicates it has been mutated") { expect(const).to be_mutated }
502
- it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
503
463
  it("indicates it has been hidden") { expect(const).to be_hidden }
504
464
  it("exposes its original value") { expect(const.original_value).to eq(:m) }
505
465
  end
@@ -511,8 +471,6 @@ module Spy
511
471
 
512
472
  it("exposes its name") { expect(const.name).to eq("TestClass::M") }
513
473
  it("indicates it was previously defined") { expect(const).to be_previously_defined }
514
- it("indicates it has been mutated") { expect(const).to be_mutated }
515
- it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
516
474
  it("indicates it has been hidden") { expect(const).to be_hidden }
517
475
  it("exposes its original value") { expect(const.original_value).to eq(:m) }
518
476
  end
@@ -4,7 +4,7 @@ module RSpec
4
4
  module Mocks
5
5
  describe "a double _not_ acting as a null object" do
6
6
  before(:each) do
7
- @double = double('non-null object')
7
+ @double = Spy.double('non-null object')
8
8
  end
9
9
 
10
10
  it "says it does not respond to messages it doesn't understand" do
@@ -17,19 +17,13 @@ module RSpec
17
17
  end
18
18
 
19
19
  it "raises an error when interpolated in a string as an integer" do
20
- # Not sure why, but 1.9.2 (but not JRuby --1.9) raises a different
21
- # error than 1.8.7 and 1.9.3...
22
- expected_error = (RUBY_VERSION == '1.9.2' && RUBY_PLATFORM !~ /java/) ?
23
- RSpec::Mocks::MockExpectationError :
24
- TypeError
25
-
26
- expect { "%i" % @double }.to raise_error(expected_error)
20
+ expect { "%i" % @double }.to raise_error(TypeError)
27
21
  end
28
22
  end
29
23
 
30
24
  describe "a double acting as a null object" do
31
25
  before(:each) do
32
- @double = double('null object').as_null_object
26
+ @double = Spy.double('null object').as_null_object
33
27
  end
34
28
 
35
29
  it "says it responds to everything" do
@@ -42,43 +36,21 @@ module RSpec
42
36
  end
43
37
 
44
38
  it "allows explicit expectation" do
45
- @double.should_receive(:something)
39
+ spy = Spy.on(@double, :something)
46
40
  @double.something
41
+ expect(spy).to have_been_called
47
42
  end
48
43
 
49
44
  it 'continues to return self from an explicit expectation' do
50
- @double.should_receive(:bar)
45
+ spy = Spy.on(@double, :bar)
51
46
  expect(@double.foo.bar).to be(@double)
47
+ expect(spy).to have_been_called
52
48
  end
53
49
 
54
50
  it 'returns an explicitly stubbed value from an expectation with no implementation' do
55
- Spy.on(@double, :foo => "bar")
56
- @double.should_receive(:foo)
51
+ spy = Spy.on(@double, :foo => "bar")
57
52
  expect(@double.foo).to eq("bar")
58
- end
59
-
60
- it "fails verification when explicit exception not met" do
61
- expect {
62
- @double.should_receive(:something)
63
- @double.rspec_verify
64
- }.to raise_error(RSpec::Mocks::MockExpectationError)
65
- end
66
-
67
- it "ignores unexpected methods" do
68
- @double.random_call("a", "d", "c")
69
- @double.rspec_verify
70
- end
71
-
72
- it "allows expected message with different args first" do
73
- @double.should_receive(:message).with(:expected_arg)
74
- @double.message(:unexpected_arg)
75
- @double.message(:expected_arg)
76
- end
77
-
78
- it "allows expected message with different args second" do
79
- @double.should_receive(:message).with(:expected_arg)
80
- @double.message(:expected_arg)
81
- @double.message(:unexpected_arg)
53
+ expect(spy).to have_been_called
82
54
  end
83
55
 
84
56
  it "can be interpolated in a string as an integer" do
@@ -92,14 +64,14 @@ module RSpec
92
64
 
93
65
  describe "#as_null_object" do
94
66
  it "sets the object to null_object" do
95
- obj = double('anything').as_null_object
67
+ obj = Spy.double('anything').as_null_object
96
68
  expect(obj).to be_null_object
97
69
  end
98
70
  end
99
71
 
100
72
  describe "#null_object?" do
101
73
  it "defaults to false" do
102
- obj = double('anything')
74
+ obj = Spy.double('anything')
103
75
  expect(obj).not_to be_null_object
104
76
  end
105
77
  end
@@ -26,72 +26,13 @@ module Spy
26
26
  expect(spy).to have_been_called
27
27
  end
28
28
 
29
- it "should_receive mocks out the method" do
30
- stub(object, :foobar).with(:test_param).and_return(1)
31
- expect(object.foobar(:test_param)).to equal(1)
32
- end
33
-
34
- it "should_receive handles a hash" do
35
- stub(object, :foobar).with(:key => "value").and_return(1)
36
- expect(object.foobar(:key => "value")).to equal(1)
37
- end
38
-
39
- it "should_receive handles an inner hash" do
40
- hash = {:a => {:key => "value"}}
41
- object.should_receive(:foobar).with(:key => "value").and_return(1)
42
- expect(object.foobar(hash[:a])).to equal(1)
43
- end
44
-
45
- it "should_receive returns a message expectation" do
46
- expect(object.should_receive(:foobar)).to be_kind_of(RSpec::Mocks::MessageExpectation)
47
- object.foobar
48
- end
49
-
50
- it "should_receive verifies method was called" do
51
- object.should_receive(:foobar).with(:test_param).and_return(1)
52
- expect {
53
- object.rspec_verify
54
- }.to raise_error(RSpec::Mocks::MockExpectationError)
55
- end
56
-
57
- it "should_receive also takes a String argument" do
58
- object.should_receive('foobar')
59
- object.foobar
60
- end
61
-
62
- it "should_not_receive also takes a String argument" do
63
- object.should_not_receive('foobar')
64
- expect {
65
- object.foobar
66
- }.to raise_error(RSpec::Mocks::MockExpectationError)
67
- end
68
-
69
29
  it "uses reports nil in the error message" do
70
30
  allow_message_expectations_on_nil
71
31
 
72
32
  _nil = nil
73
- _nil.should_receive(:foobar)
74
- expect {
75
- _nil.rspec_verify
76
- }.to raise_error(
77
- RSpec::Mocks::MockExpectationError,
78
- %Q|(nil).foobar(any args)\n expected: 1 time\n received: 0 times|
79
- )
80
- end
81
-
82
- it "includes the class name in the error when mocking a class method that is called an extra time with the wrong args" do
83
- klass = Class.new do
84
- def self.inspect
85
- "MyClass"
86
- end
87
- end
88
-
89
- klass.should_receive(:bar).with(1)
90
- klass.bar(1)
91
-
92
- expect {
93
- klass.bar(2)
94
- }.to raise_error(RSpec::Mocks::MockExpectationError, /MyClass/)
33
+ spy = stub(_nil, :foobar)
34
+ _nil.foobar
35
+ expect(spy).to have_been_called
95
36
  end
96
37
  end
97
38
 
@@ -1,54 +1,47 @@
1
1
  require 'spec_helper'
2
2
 
3
- module RSpec
4
- module Mocks
5
-
6
- describe "only stashing the original method" do
7
- let(:klass) do
8
- Class.new do
9
- def self.foo(arg)
10
- :original_value
11
- end
3
+ module Spy
4
+ describe "only stashing the original method" do
5
+ let(:klass) do
6
+ Class.new do
7
+ def self.foo(arg)
8
+ :original_value
12
9
  end
13
10
  end
11
+ end
14
12
 
15
- it "keeps the original method intact after multiple expectations are added on the same method" do
16
- klass.should_receive(:foo).with(:fizbaz).and_return(:wowwow)
17
- klass.should_receive(:foo).with(:bazbar).and_return(:okay)
18
-
19
- klass.foo(:fizbaz)
20
- klass.foo(:bazbar)
21
- klass.rspec_verify
13
+ it "keeps the original method intact after multiple expectations are added on the same method" do
14
+ spy = Spy.on(klass, :foo)
15
+ klass.foo(:bazbar)
16
+ expect(spy).to have_been_called
17
+ Spy.off(klass, :foo)
22
18
 
23
- klass.rspec_reset
24
- expect(klass.foo(:yeah)).to equal(:original_value)
25
- end
19
+ expect(klass.foo(:yeah)).to equal(:original_value)
26
20
  end
21
+ end
27
22
 
28
- describe "when a class method is aliased on a subclass and the method is mocked" do
29
- let(:klass) do
30
- Class.new do
31
- class << self
32
- alias alternate_new new
33
- end
23
+ describe "when a class method is aliased on a subclass and the method is mocked" do
24
+ let(:klass) do
25
+ Class.new do
26
+ class << self
27
+ alias alternate_new new
34
28
  end
35
29
  end
30
+ end
36
31
 
37
- it "restores the original aliased public method" do
38
- klass = Class.new do
39
- class << self
40
- alias alternate_new new
41
- end
32
+ it "restores the original aliased public method" do
33
+ klass = Class.new do
34
+ class << self
35
+ alias alternate_new new
42
36
  end
37
+ end
43
38
 
44
- klass.should_receive(:alternate_new)
45
- expect(klass.alternate_new).to be_nil
46
-
47
- klass.rspec_verify
39
+ spy = Spy.on(klass, :alternate_new)
40
+ expect(klass.alternate_new).to be_nil
41
+ expect(spy).to have_been_called
48
42
 
49
- klass.rspec_reset
50
- expect(klass.alternate_new).to be_an_instance_of(klass)
51
- end
43
+ Spy.off(klass, :alternate_new)
44
+ expect(klass.alternate_new).to be_an_instance_of(klass)
52
45
  end
53
46
  end
54
47
  end
@@ -46,12 +46,6 @@ module RSpec
46
46
  expect(@class).to respond_to(:msg)
47
47
  end
48
48
 
49
- it "handles multiple stubbed methods" do
50
- Spy::Subroutine.new(@instance, :msg1 => 1, :msg2 => 2).hook(force: true)
51
- expect(@instance.msg1).to eq(1)
52
- expect(@instance.msg2).to eq(2)
53
- end
54
-
55
49
  it "yields a specified object" do
56
50
  Spy::Subroutine.new(@instance, :method_that_yields).hook(force: true).and_yield(:yielded_obj)
57
51
  current_value = :value_before
@@ -13,28 +13,28 @@ describe "a double receiving to_ary" do
13
13
  end
14
14
 
15
15
  it "can be overridden with a stub" do
16
- Spy.on(obj, :to_ary) { :non_nil_value }
16
+ Spy::Subroutine.new(obj, :to_ary).hook(force: true).and_return(:non_nil_value)
17
17
  expect(obj.to_ary).to be(:non_nil_value)
18
18
  end
19
19
 
20
20
  it "responds when overriden" do
21
- Spy.on(obj, :to_ary) { :non_nil_value }
21
+ Spy::Subroutine.new(obj, :to_ary).hook(force: true).and_return(:non_nil_value)
22
22
  expect(obj).to respond_to(:to_ary)
23
23
  end
24
24
 
25
25
  it "supports Array#flatten" do
26
- obj = double('foo')
26
+ obj = Spy.double('foo')
27
27
  expect([obj].flatten).to eq([obj])
28
28
  end
29
29
  end
30
30
 
31
31
  context "double as_null_object" do
32
- let(:obj) { double('obj').as_null_object }
32
+ let(:obj) { Spy.double('obj').as_null_object }
33
33
  include_examples "to_ary"
34
34
  end
35
35
 
36
36
  context "double without as_null_object" do
37
- let(:obj) { double('obj') }
37
+ let(:obj) { Spy.double('obj') }
38
38
  include_examples "to_ary"
39
39
  end
40
40
  end