spy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +6 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +133 -0
  5. data/Rakefile +8 -0
  6. data/TODO.md +8 -0
  7. data/lib/spy.rb +259 -0
  8. data/lib/spy/double.rb +11 -0
  9. data/lib/spy/dsl.rb +7 -0
  10. data/lib/spy/version.rb +3 -0
  11. data/spec/spec_helper.rb +39 -0
  12. data/spec/spy/and_call_original_spec.rb +152 -0
  13. data/spec/spy/and_yield_spec.rb +114 -0
  14. data/spec/spy/bug_report_10260_spec.rb +8 -0
  15. data/spec/spy/bug_report_10263_spec.rb +24 -0
  16. data/spec/spy/bug_report_496_spec.rb +18 -0
  17. data/spec/spy/bug_report_600_spec.rb +24 -0
  18. data/spec/spy/bug_report_7611_spec.rb +16 -0
  19. data/spec/spy/bug_report_8165_spec.rb +31 -0
  20. data/spec/spy/bug_report_830_spec.rb +21 -0
  21. data/spec/spy/bug_report_957_spec.rb +22 -0
  22. data/spec/spy/double_spec.rb +12 -0
  23. data/spec/spy/failing_argument_matchers_spec.rb +94 -0
  24. data/spec/spy/hash_excluding_matcher_spec.rb +67 -0
  25. data/spec/spy/hash_including_matcher_spec.rb +90 -0
  26. data/spec/spy/mock_spec.rb +734 -0
  27. data/spec/spy/multiple_return_value_spec.rb +119 -0
  28. data/spec/spy/mutate_const_spec.rb +481 -0
  29. data/spec/spy/nil_expectation_warning_spec.rb +56 -0
  30. data/spec/spy/null_object_mock_spec.rb +107 -0
  31. data/spec/spy/options_hash_spec.rb +35 -0
  32. data/spec/spy/partial_mock_spec.rb +196 -0
  33. data/spec/spy/passing_argument_matchers_spec.rb +142 -0
  34. data/spec/spy/precise_counts_spec.rb +68 -0
  35. data/spec/spy/serialization_spec.rb +110 -0
  36. data/spec/spy/stash_spec.rb +54 -0
  37. data/spec/spy/stub_implementation_spec.rb +62 -0
  38. data/spec/spy/stub_spec.rb +85 -0
  39. data/spec/spy/stubbed_message_expectations_spec.rb +47 -0
  40. data/spec/spy/test_double_spec.rb +57 -0
  41. data/spec/spy/to_ary_spec.rb +40 -0
  42. data/spy.gemspec +21 -0
  43. data/test/spy/test_double.rb +19 -0
  44. data/test/test_helper.rb +6 -0
  45. data/test/test_spy.rb +258 -0
  46. metadata +157 -0
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ describe "PreciseCounts" do
6
+ before(:each) do
7
+ @double = double("test double")
8
+ end
9
+
10
+ it "fails when exactly n times method is called less than n times" do
11
+ @double.should_receive(:do_something).exactly(3).times
12
+ @double.do_something
13
+ @double.do_something
14
+ expect {
15
+ @double.rspec_verify
16
+ }.to raise_error(RSpec::Mocks::MockExpectationError)
17
+ end
18
+
19
+ it "fails fast when exactly n times method is called more than n times" do
20
+ @double.should_receive(:do_something).exactly(3).times
21
+ @double.do_something
22
+ @double.do_something
23
+ @double.do_something
24
+ expect {
25
+ @double.do_something
26
+ }.to raise_error(RSpec::Mocks::MockExpectationError)
27
+ end
28
+
29
+ it "fails when exactly n times method is never called" do
30
+ @double.should_receive(:do_something).exactly(3).times
31
+ expect {
32
+ @double.rspec_verify
33
+ }.to raise_error(RSpec::Mocks::MockExpectationError)
34
+ end
35
+
36
+ it "passes if exactly n times method is called exactly n times" do
37
+ @double.should_receive(:do_something).exactly(3).times
38
+ @double.do_something
39
+ @double.do_something
40
+ @double.do_something
41
+ @double.rspec_verify
42
+ end
43
+
44
+ it "returns the value given by a block when the exactly once method is called" do
45
+ @double.should_receive(:to_s).exactly(:once) { "testing" }
46
+ expect(@double.to_s).to eq "testing"
47
+ @double.rspec_verify
48
+ end
49
+
50
+ it "passes mutiple calls with different args" do
51
+ @double.should_receive(:do_something).once.with(1)
52
+ @double.should_receive(:do_something).once.with(2)
53
+ @double.do_something(1)
54
+ @double.do_something(2)
55
+ @double.rspec_verify
56
+ end
57
+
58
+ it "passes multiple calls with different args and counts" do
59
+ @double.should_receive(:do_something).twice.with(1)
60
+ @double.should_receive(:do_something).once.with(2)
61
+ @double.do_something(1)
62
+ @double.do_something(2)
63
+ @double.do_something(1)
64
+ @double.rspec_verify
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,110 @@
1
+ require 'spec_helper'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ describe Serialization do
6
+
7
+ class SerializableObject < Struct.new(:foo, :bar); end
8
+
9
+ class SerializableMockProxy
10
+ attr_reader :mock_proxy
11
+
12
+ def initialize(mock_proxy)
13
+ @mock_proxy = mock_proxy
14
+ end
15
+
16
+ def ==(other)
17
+ other.class == self.class && other.mock_proxy == mock_proxy
18
+ end
19
+ end
20
+
21
+ def self.with_yaml_loaded(&block)
22
+ context 'with YAML loaded' do
23
+ module_eval(&block)
24
+ end
25
+ end
26
+
27
+ def self.without_yaml_loaded(&block)
28
+ context 'without YAML loaded' do
29
+ before do
30
+ # We can't really unload yaml, but we can fake it here...
31
+ hide_const("YAML")
32
+ Struct.class_eval do
33
+ alias __old_to_yaml to_yaml
34
+ undef to_yaml
35
+ end
36
+ end
37
+
38
+ module_eval(&block)
39
+
40
+ after do
41
+ Struct.class_eval do
42
+ alias to_yaml __old_to_yaml
43
+ undef __old_to_yaml
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ let(:serializable_object) { RSpec::Mocks::SerializableObject.new(7, "something") }
50
+
51
+ def set_stub
52
+ Spy.on(serializable_object, :bazz => 5)
53
+ end
54
+
55
+ shared_examples_for 'normal YAML serialization' do
56
+ it 'serializes to yaml the same with and without stubbing, using #to_yaml' do
57
+ expect { set_stub }.to_not change { serializable_object.to_yaml }
58
+ end
59
+
60
+ it 'serializes to yaml the same with and without stubbing, using YAML.dump' do
61
+ expect { set_stub }.to_not change { ::YAML.dump(serializable_object) }
62
+ end
63
+ end
64
+
65
+ with_yaml_loaded do
66
+ compiled_with_psych = begin
67
+ require 'psych'
68
+ true
69
+ rescue LoadError
70
+ false
71
+ end
72
+
73
+ if compiled_with_psych
74
+ context 'using Syck as the YAML engine' do
75
+ before(:each) { ::YAML::ENGINE.yamler = 'syck' }
76
+ it_behaves_like 'normal YAML serialization'
77
+ end
78
+
79
+ context 'using Psych as the YAML engine' do
80
+ before(:each) { ::YAML::ENGINE.yamler = 'psych' }
81
+ it_behaves_like 'normal YAML serialization'
82
+ end
83
+ else
84
+ it_behaves_like 'normal YAML serialization'
85
+ end
86
+ end
87
+
88
+ without_yaml_loaded do
89
+ it 'does not add #to_yaml to the stubbed object' do
90
+ expect(serializable_object).not_to respond_to(:to_yaml)
91
+ set_stub
92
+ expect(serializable_object).not_to respond_to(:to_yaml)
93
+ end
94
+ end
95
+
96
+ it 'marshals the same with and without stubbing' do
97
+ expect { set_stub }.to_not change { Marshal.dump(serializable_object) }
98
+ end
99
+
100
+ describe "an object that has its own mock_proxy instance variable" do
101
+ let(:serializable_object) { RSpec::Mocks::SerializableMockProxy.new(:my_mock_proxy) }
102
+
103
+ it 'does not interfere with its marshalling' do
104
+ marshalled_copy = Marshal.load(Marshal.dump(serializable_object))
105
+ expect(marshalled_copy).to eq serializable_object
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
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
12
+ end
13
+ end
14
+
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
22
+
23
+ klass.rspec_reset
24
+ expect(klass.foo(:yeah)).to equal(:original_value)
25
+ end
26
+ end
27
+
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
34
+ end
35
+ end
36
+
37
+ it "restores the original aliased public method" do
38
+ klass = Class.new do
39
+ class << self
40
+ alias alternate_new new
41
+ end
42
+ end
43
+
44
+ klass.should_receive(:alternate_new)
45
+ expect(klass.alternate_new).to be_nil
46
+
47
+ klass.rspec_verify
48
+
49
+ klass.rspec_reset
50
+ expect(klass.alternate_new).to be_an_instance_of(klass)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ describe "stub implementation" do
6
+ describe "with no args" do
7
+ it "execs the block when called" do
8
+ obj = stub()
9
+ Spy.stub(obj, :foo).and_return { :bar }
10
+ expect(obj.foo).to eq :bar
11
+ end
12
+ end
13
+
14
+ describe "with one arg" do
15
+ it "execs the block with that arg when called" do
16
+ obj = stub()
17
+ Spy.stub(obj, :foo).and_return {|given| given}
18
+ expect(obj.foo(:bar)).to eq :bar
19
+ end
20
+ end
21
+
22
+ describe "with variable args" do
23
+ it "execs the block when called" do
24
+ obj = stub()
25
+ Spy.stub(obj, :foo).and_return {|*given| given.first}
26
+ expect(obj.foo(:bar)).to eq :bar
27
+ end
28
+ end
29
+ end
30
+
31
+
32
+ describe "unstub implementation" do
33
+ it "replaces the stubbed method with the original method" do
34
+ obj = Object.new
35
+ def obj.foo; :original; end
36
+ Spy.stub(obj, :foo)
37
+ Spy.off(obj, :foo)
38
+ expect(obj.foo).to eq :original
39
+ end
40
+
41
+ it "restores the correct implementations when stubbed and unstubbed on a parent and child class" do
42
+ parent = Class.new
43
+ child = Class.new(parent)
44
+
45
+ Spy.stub(parent, :new)
46
+ Spy.stub(child, :new)
47
+ Spy.off(parent, :new)
48
+ Spy.off(child, :new)
49
+
50
+ expect(parent.new).to be_an_instance_of parent
51
+ expect(child.new).to be_an_instance_of child
52
+ end
53
+
54
+ it "raises a MockExpectationError if the method has not been stubbed" do
55
+ obj = Object.new
56
+ expect {
57
+ Spy.off(obj, :foo)
58
+ }.to raise_error
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ describe "A method stub" do
6
+ before(:each) do
7
+ @class = Class.new do
8
+ class << self
9
+ def existing_class_method
10
+ existing_private_class_method
11
+ end
12
+
13
+ private
14
+ def existing_private_class_method
15
+ :original_value
16
+ end
17
+ end
18
+
19
+ def existing_instance_method
20
+ existing_private_instance_method
21
+ end
22
+
23
+ private
24
+ def existing_private_instance_method
25
+ :original_value
26
+ end
27
+ end
28
+ @instance = @class.new
29
+ @stub = Object.new
30
+ end
31
+
32
+ describe "using Spy.stub" do
33
+ it "returns declared value when message is received" do
34
+ Spy.stub(@instance, :msg).and_return(:return_value)
35
+ expect(@instance.msg).to equal(:return_value)
36
+ end
37
+ end
38
+
39
+ it "instructs an instance to respond_to the message" do
40
+ Spy.stub(@instance, :msg)
41
+ expect(@instance).to respond_to(:msg)
42
+ end
43
+
44
+ it "instructs a class object to respond_to the message" do
45
+ Spy.stub(@class, :msg)
46
+ expect(@class).to respond_to(:msg)
47
+ end
48
+
49
+ it "handles multiple stubbed methods" do
50
+ Spy.stub(@instance, :msg1 => 1, :msg2 => 2)
51
+ expect(@instance.msg1).to eq(1)
52
+ expect(@instance.msg2).to eq(2)
53
+ end
54
+
55
+ it "yields a specified object" do
56
+ Spy.stub(@instance, :method_that_yields).and_yield(:yielded_obj)
57
+ current_value = :value_before
58
+ @instance.method_that_yields {|val| current_value = val}
59
+ expect(current_value).to eq :yielded_obj
60
+ end
61
+
62
+ it "yields a specified object and return another specified object" do
63
+ yielded_obj = double("my mock")
64
+ Spy.stub(yielded_obj, :foo)
65
+ Spy.stub(@instance, :method_that_yields_and_returns).and_yield(yielded_obj).and_return(:baz)
66
+ expect(@instance.method_that_yields_and_returns { |o| o.foo :bar }).to eq :baz
67
+ end
68
+
69
+ it "throws when told to" do
70
+ Spy.stub(@stub, :something).and_throw(:up)
71
+ expect { @stub.something }.to throw_symbol(:up)
72
+ end
73
+
74
+ it "throws with argument when told to" do
75
+ Spy.stub(@stub, :something).and_throw(:up, 'high')
76
+ expect { @stub.something }.to throw_symbol(:up, 'high')
77
+ end
78
+
79
+ it "overrides a pre-existing method" do
80
+ Spy.stub(@stub, :existing_instance_method).and_return(:updated_stub_value)
81
+ expect(@stub.existing_instance_method).to eq :updated_stub_value
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe "expection set on previously stubbed method" do
4
+ it "fails if message is not received after expectation is set" do
5
+ double = double(:msg => nil)
6
+ double.msg
7
+ double.should_receive(:msg)
8
+ expect { double.rspec_verify }.to raise_error(RSpec::Mocks::MockExpectationError)
9
+ end
10
+
11
+ it "outputs arguments of similar calls" do
12
+ double = double('double', :foo => true)
13
+ double.should_receive(:foo).with('first')
14
+ double.foo('second')
15
+ double.foo('third')
16
+ expect {
17
+ double.rspec_verify
18
+ }.to raise_error(%Q|Double "double" received :foo with unexpected arguments\n expected: ("first")\n got: ("second"), ("third")|)
19
+ double.rspec_reset
20
+ end
21
+
22
+ context "with argument constraint on stub" do
23
+ it "matches any args if no arg constraint set on expectation" do
24
+ double = double("mock")
25
+ Spy.on(double, :foo).with(3).and_return("stub")
26
+ double.should_receive(:foo).at_least(:once).and_return("expectation")
27
+ double.foo
28
+ double.rspec_verify
29
+ end
30
+
31
+ it "matches specific args set on expectation" do
32
+ double = double("mock")
33
+ Spy.on(double, :foo).with(3).and_return("stub")
34
+ double.should_receive(:foo).at_least(:once).with(4).and_return("expectation")
35
+ double.foo(4)
36
+ double.rspec_verify
37
+ end
38
+
39
+ it "fails if expectation's arg constraint is not met" do
40
+ double = double("mock")
41
+ Spy.on(double, :foo).with(3).and_return("stub")
42
+ double.should_receive(:foo).at_least(:once).with(4).and_return("expectation")
43
+ double.foo(3)
44
+ expect { double.rspec_verify }.to raise_error(/expected: \(4\)\s+got: \(3\)/)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ describe TestDouble do
6
+ before(:all) do
7
+ Module.class_eval do
8
+ private
9
+ def use; end
10
+ end
11
+ end
12
+
13
+ after(:all) do
14
+ Module.class_eval do
15
+ undef use
16
+ end
17
+ end
18
+
19
+ it 'can be extended onto a module to make it a pure test double that can mock private methods' do
20
+ double = Module.new
21
+ Spy.on(double, :use)
22
+ expect { double.use }.to raise_error(/private method `use' called/)
23
+
24
+ double = Module.new { TestDouble.extend_onto(self) }
25
+ double.should_receive(:use).and_return(:ok)
26
+ expect(double.use).to be(:ok)
27
+ end
28
+
29
+ it 'sets the test double name when a name is passed' do
30
+ double = Module.new { TestDouble.extend_onto(self, "MyDouble") }
31
+ expect { double.foo }.to raise_error(/Mock "MyDouble" received/)
32
+ end
33
+
34
+ it 'stubs the methods passed in the stubs hash' do
35
+ double = Module.new do
36
+ TestDouble.extend_onto(self, "MyDouble", :a => 5, :b => 10)
37
+ end
38
+
39
+ expect(double.a).to eq(5)
40
+ expect(double.b).to eq(10)
41
+ end
42
+
43
+ it 'indicates what type of test double it is in error messages' do
44
+ double = Module.new do
45
+ TestDouble.extend_onto(self, "A", :__declared_as => "ModuleMock")
46
+ end
47
+ expect { double.foo }.to raise_error(/ModuleMock "A"/)
48
+ end
49
+
50
+ it 'is declared as a mock by default' do
51
+ double = Module.new { TestDouble.extend_onto(self) }
52
+ expect { double.foo }.to raise_error(/Mock received/)
53
+ end
54
+ end
55
+ end
56
+ end
57
+