spy 0.0.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 (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,56 @@
1
+ require 'spec_helper'
2
+
3
+ def remove_last_describe_from_world
4
+ RSpec::world.example_groups.pop
5
+ end
6
+
7
+ def empty_example_group
8
+ RSpec::Core::ExampleGroup.describe(Object, 'Empty Behaviour Group') { }
9
+ remove_last_describe_from_world
10
+ end
11
+
12
+ module RSpec
13
+ module Mocks
14
+ describe "an expectation set on nil" do
15
+ it "issues a warning with file and line number information" do
16
+ expected_warning = %r%An expectation of :foo was set on nil. Called from #{__FILE__}:#{__LINE__+3}(:in .+)?. Use allow_message_expectations_on_nil to disable warnings.%
17
+ Kernel.should_receive(:warn).with(expected_warning)
18
+
19
+ nil.should_receive(:foo)
20
+ nil.foo
21
+ end
22
+
23
+ it "issues a warning when the expectation is negative" do
24
+ Kernel.should_receive(:warn)
25
+
26
+ nil.should_not_receive(:foo)
27
+ end
28
+
29
+ it "does not issue a warning when expectations are set to be allowed" do
30
+ allow_message_expectations_on_nil
31
+ Kernel.should_not_receive(:warn)
32
+
33
+ nil.should_receive(:foo)
34
+ nil.should_not_receive(:bar)
35
+ nil.foo
36
+ end
37
+ end
38
+
39
+ describe "#allow_message_expectations_on_nil" do
40
+ it "does not effect subsequent examples" do
41
+ example_group = empty_example_group
42
+ example_group.it("when called in one example that doesn't end up setting an expectation on nil") do
43
+ allow_message_expectations_on_nil
44
+ end
45
+ example_group.it("should not effect the next exapmle ran") do
46
+ Kernel.should_receive(:warn)
47
+ nil.should_receive(:foo)
48
+ nil.foo
49
+ end
50
+
51
+ example_group
52
+ end
53
+ end
54
+ end
55
+ end
56
+
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ describe "a double _not_ acting as a null object" do
6
+ before(:each) do
7
+ @double = double('non-null object')
8
+ end
9
+
10
+ it "says it does not respond to messages it doesn't understand" do
11
+ expect(@double).not_to respond_to(:foo)
12
+ end
13
+
14
+ it "says it responds to messages it does understand" do
15
+ Spy.on(@double, :foo)
16
+ expect(@double).to respond_to(:foo)
17
+ end
18
+
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)
27
+ end
28
+ end
29
+
30
+ describe "a double acting as a null object" do
31
+ before(:each) do
32
+ @double = double('null object').as_null_object
33
+ end
34
+
35
+ it "says it responds to everything" do
36
+ expect(@double).to respond_to(:any_message_it_gets)
37
+ end
38
+
39
+ it "allows explicit stubs" do
40
+ Spy.on(@double, :foo) { "bar" }
41
+ expect(@double.foo).to eq("bar")
42
+ end
43
+
44
+ it "allows explicit expectation" do
45
+ @double.should_receive(:something)
46
+ @double.something
47
+ end
48
+
49
+ it 'continues to return self from an explicit expectation' do
50
+ @double.should_receive(:bar)
51
+ expect(@double.foo.bar).to be(@double)
52
+ end
53
+
54
+ it 'returns an explicitly stubbed value from an expectation with no implementation' do
55
+ Spy.on(@double, :foo => "bar")
56
+ @double.should_receive(:foo)
57
+ 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)
82
+ end
83
+
84
+ it "can be interpolated in a string as an integer" do
85
+ # This form of string interpolation calls
86
+ # @double.to_int.to_int.to_int...etc until it gets an integer,
87
+ # and thus gets stuck in an infinite loop unless our double
88
+ # returns an int value from #to_int.
89
+ expect(("%i" % @double)).to eq("0")
90
+ end
91
+ end
92
+
93
+ describe "#as_null_object" do
94
+ it "sets the object to null_object" do
95
+ obj = double('anything').as_null_object
96
+ expect(obj).to be_null_object
97
+ end
98
+ end
99
+
100
+ describe "#null_object?" do
101
+ it "defaults to false" do
102
+ obj = double('anything')
103
+ expect(obj).not_to be_null_object
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ describe "calling :should_receive with an options hash" do
6
+ it "reports the file and line submitted with :expected_from" do
7
+ begin
8
+ mock = RSpec::Mocks::Mock.new("a mock")
9
+ mock.should_receive(:message, :expected_from => "/path/to/blah.ext:37")
10
+ mock.rspec_verify
11
+ rescue Exception => e
12
+ ensure
13
+ expect(e.backtrace.to_s).to match /\/path\/to\/blah.ext:37/m
14
+ end
15
+ end
16
+
17
+ it "uses the message supplied with :message" do
18
+ expect {
19
+ m = RSpec::Mocks::Mock.new("a mock")
20
+ m.should_receive(:message, :message => "recebi nada")
21
+ m.rspec_verify
22
+ }.to raise_error("recebi nada")
23
+ end
24
+
25
+ it "uses the message supplied with :message after a similar stub" do
26
+ expect {
27
+ m = RSpec::Mocks::Mock.new("a mock")
28
+ Spy.on(m, :message)
29
+ m.should_receive(:message, :message => "from mock")
30
+ m.rspec_verify
31
+ }.to raise_error("from mock")
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,196 @@
1
+ require 'spec_helper'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ describe "using a Partial Mock," do
6
+ let(:object) { Object.new }
7
+
8
+ it "names the class in the failure message" do
9
+ object.should_receive(:foo)
10
+ expect do
11
+ object.rspec_verify
12
+ end.to raise_error(RSpec::Mocks::MockExpectationError, /\(#<Object:.*>\).foo/)
13
+ end
14
+
15
+ it "names the class in the failure message when expectation is on class" do
16
+ Object.should_receive(:foo)
17
+ expect {
18
+ Object.rspec_verify
19
+ }.to raise_error(RSpec::Mocks::MockExpectationError, /<Object \(class\)>/)
20
+ end
21
+
22
+ it "does not conflict with @options in the object" do
23
+ object.instance_eval { @options = Object.new }
24
+ object.should_receive(:blah)
25
+ object.blah
26
+ end
27
+
28
+ it "should_not_receive mocks out the method" do
29
+ object.should_not_receive(:fuhbar)
30
+ expect {
31
+ object.fuhbar
32
+ }.to raise_error(
33
+ RSpec::Mocks::MockExpectationError,
34
+ /expected\: 0 times\n received\: 1 time/
35
+ )
36
+ end
37
+
38
+ it "should_not_receive returns a negative message expectation" do
39
+ expect(object.should_not_receive(:foobar)).to be_kind_of(RSpec::Mocks::NegativeMessageExpectation)
40
+ end
41
+
42
+ it "should_receive mocks out the method" do
43
+ object.should_receive(:foobar).with(:test_param).and_return(1)
44
+ expect(object.foobar(:test_param)).to equal(1)
45
+ end
46
+
47
+ it "should_receive handles a hash" do
48
+ object.should_receive(:foobar).with(:key => "value").and_return(1)
49
+ expect(object.foobar(:key => "value")).to equal(1)
50
+ end
51
+
52
+ it "should_receive handles an inner hash" do
53
+ hash = {:a => {:key => "value"}}
54
+ object.should_receive(:foobar).with(:key => "value").and_return(1)
55
+ expect(object.foobar(hash[:a])).to equal(1)
56
+ end
57
+
58
+ it "should_receive returns a message expectation" do
59
+ expect(object.should_receive(:foobar)).to be_kind_of(RSpec::Mocks::MessageExpectation)
60
+ object.foobar
61
+ end
62
+
63
+ it "should_receive verifies method was called" do
64
+ object.should_receive(:foobar).with(:test_param).and_return(1)
65
+ expect {
66
+ object.rspec_verify
67
+ }.to raise_error(RSpec::Mocks::MockExpectationError)
68
+ end
69
+
70
+ it "should_receive also takes a String argument" do
71
+ object.should_receive('foobar')
72
+ object.foobar
73
+ end
74
+
75
+ it "should_not_receive also takes a String argument" do
76
+ object.should_not_receive('foobar')
77
+ expect {
78
+ object.foobar
79
+ }.to raise_error(RSpec::Mocks::MockExpectationError)
80
+ end
81
+
82
+ it "uses reports nil in the error message" do
83
+ allow_message_expectations_on_nil
84
+
85
+ _nil = nil
86
+ _nil.should_receive(:foobar)
87
+ expect {
88
+ _nil.rspec_verify
89
+ }.to raise_error(
90
+ RSpec::Mocks::MockExpectationError,
91
+ %Q|(nil).foobar(any args)\n expected: 1 time\n received: 0 times|
92
+ )
93
+ end
94
+
95
+ it "includes the class name in the error when mocking a class method that is called an extra time with the wrong args" do
96
+ klass = Class.new do
97
+ def self.inspect
98
+ "MyClass"
99
+ end
100
+ end
101
+
102
+ klass.should_receive(:bar).with(1)
103
+ klass.bar(1)
104
+
105
+ expect {
106
+ klass.bar(2)
107
+ }.to raise_error(RSpec::Mocks::MockExpectationError, /MyClass/)
108
+ end
109
+ end
110
+
111
+ describe "Using a partial mock on a proxy object", :if => defined?(::BasicObject) do
112
+ let(:proxy_class) do
113
+ Class.new(::BasicObject) do
114
+ def initialize(target)
115
+ @target = target
116
+ end
117
+
118
+ def proxied?
119
+ true
120
+ end
121
+
122
+ def method_missing(*a)
123
+ @target.send(*a)
124
+ end
125
+ end
126
+ end
127
+
128
+ let(:instance) { proxy_class.new(Object.new) }
129
+
130
+ it 'works properly' do
131
+ instance.should_receive(:proxied?).and_return(false)
132
+ expect(instance).not_to be_proxied
133
+ end
134
+ end
135
+
136
+ describe "Partially mocking an object that defines ==, after another mock has been defined" do
137
+ before(:each) do
138
+ stub("existing mock", :foo => :foo)
139
+ end
140
+
141
+ let(:klass) do
142
+ Class.new do
143
+ attr_reader :val
144
+ def initialize(val)
145
+ @val = val
146
+ end
147
+
148
+ def ==(other)
149
+ @val == other.val
150
+ end
151
+ end
152
+ end
153
+
154
+ it "does not raise an error when stubbing the object" do
155
+ o = klass.new :foo
156
+ expect { Spy.on(o, :bar) }.not_to raise_error(NoMethodError)
157
+ end
158
+ end
159
+
160
+ describe "Method visibility when using partial mocks" do
161
+ let(:klass) do
162
+ Class.new do
163
+ def public_method
164
+ private_method
165
+ protected_method
166
+ end
167
+ protected
168
+ def protected_method; end
169
+ private
170
+ def private_method; end
171
+ end
172
+ end
173
+
174
+ let(:object) { klass.new }
175
+
176
+ it 'keeps public methods public' do
177
+ object.should_receive(:public_method)
178
+ expect(object.public_methods).to include_method(:public_method)
179
+ object.public_method
180
+ end
181
+
182
+ it 'keeps private methods private' do
183
+ object.should_receive(:private_method)
184
+ expect(object.private_methods).to include_method(:private_method)
185
+ object.public_method
186
+ end
187
+
188
+ it 'keeps protected methods protected' do
189
+ object.should_receive(:protected_method)
190
+ expect(object.protected_methods).to include_method(:protected_method)
191
+ object.public_method
192
+ end
193
+
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,142 @@
1
+ require 'spec_helper'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ describe Methods, :broken do
6
+ before(:each) do
7
+ @double = double('double')
8
+ Spy.on(Kernel, :warn)
9
+ end
10
+
11
+ after(:each) do
12
+ @double.rspec_verify
13
+ end
14
+
15
+ context "handling argument matchers" do
16
+ it "accepts true as boolean()" do
17
+ @double.should_receive(:random_call).with(boolean())
18
+ @double.random_call(true)
19
+ end
20
+
21
+ it "accepts false as boolean()" do
22
+ @double.should_receive(:random_call).with(boolean())
23
+ @double.random_call(false)
24
+ end
25
+
26
+ it "accepts fixnum as kind_of(Numeric)" do
27
+ @double.should_receive(:random_call).with(kind_of(Numeric))
28
+ @double.random_call(1)
29
+ end
30
+
31
+ it "accepts float as an_instance_of(Numeric)" do
32
+ @double.should_receive(:random_call).with(kind_of(Numeric))
33
+ @double.random_call(1.5)
34
+ end
35
+
36
+ it "accepts fixnum as instance_of(Fixnum)" do
37
+ @double.should_receive(:random_call).with(instance_of(Fixnum))
38
+ @double.random_call(1)
39
+ end
40
+
41
+ it "does NOT accept fixnum as instance_of(Numeric)" do
42
+ @double.should_not_receive(:random_call).with(instance_of(Numeric))
43
+ @double.random_call(1)
44
+ end
45
+
46
+ it "does NOT accept float as instance_of(Numeric)" do
47
+ @double.should_not_receive(:random_call).with(instance_of(Numeric))
48
+ @double.random_call(1.5)
49
+ end
50
+
51
+ it "accepts string as anything()" do
52
+ @double.should_receive(:random_call).with("a", anything(), "c")
53
+ @double.random_call("a", "whatever", "c")
54
+ end
55
+
56
+ it "matches duck type with one method" do
57
+ @double.should_receive(:random_call).with(duck_type(:length))
58
+ @double.random_call([])
59
+ end
60
+
61
+ it "matches duck type with two methods" do
62
+ @double.should_receive(:random_call).with(duck_type(:abs, :div))
63
+ @double.random_call(1)
64
+ end
65
+
66
+ it "matches no args against any_args()" do
67
+ @double.should_receive(:random_call).with(any_args)
68
+ @double.random_call()
69
+ end
70
+
71
+ it "matches one arg against any_args()" do
72
+ @double.should_receive(:random_call).with(any_args)
73
+ @double.random_call("a string")
74
+ end
75
+
76
+ it "matches no args against no_args()" do
77
+ @double.should_receive(:random_call).with(no_args)
78
+ @double.random_call()
79
+ end
80
+
81
+ it "matches hash with hash_including same hash" do
82
+ @double.should_receive(:random_call).with(hash_including(:a => 1))
83
+ @double.random_call(:a => 1)
84
+ end
85
+ end
86
+
87
+ context "handling block matchers" do
88
+ it "matches arguments against RSpec expectations" do
89
+ @double.should_receive(:random_call).with {|arg1, arg2, arr, *rest|
90
+ expect(arg1).to eq 5
91
+ expect(arg2).to have_at_least(3).characters
92
+ expect(arg2).to have_at_most(10).characters
93
+ expect(arr.map {|i| i * 2}).to eq [2,4,6]
94
+ expect(rest).to eq [:fee, "fi", 4]
95
+ }
96
+ @double.random_call 5, "hello", [1,2,3], :fee, "fi", 4
97
+ end
98
+
99
+ it "does not eval the block as the return value" do
100
+ eval_count = 0
101
+ @double.should_receive(:msg).with {|a| eval_count += 1}
102
+ @double.msg(:ignore)
103
+ expect(eval_count).to eq(1)
104
+ end
105
+ end
106
+
107
+ context "handling non-matcher arguments" do
108
+ it "matches non special symbol (can be removed when deprecated symbols are removed)" do
109
+ @double.should_receive(:random_call).with(:some_symbol)
110
+ @double.random_call(:some_symbol)
111
+ end
112
+
113
+ it "matches string against regexp" do
114
+ @double.should_receive(:random_call).with(/bcd/)
115
+ @double.random_call("abcde")
116
+ end
117
+
118
+ it "matches regexp against regexp" do
119
+ @double.should_receive(:random_call).with(/bcd/)
120
+ @double.random_call(/bcd/)
121
+ end
122
+
123
+ it "matches against a hash submitted and received by value" do
124
+ @double.should_receive(:random_call).with(:a => "a", :b => "b")
125
+ @double.random_call(:a => "a", :b => "b")
126
+ end
127
+
128
+ it "matches against a hash submitted by reference and received by value" do
129
+ opts = {:a => "a", :b => "b"}
130
+ @double.should_receive(:random_call).with(opts)
131
+ @double.random_call(:a => "a", :b => "b")
132
+ end
133
+
134
+ it "matches against a hash submitted by value and received by reference" do
135
+ opts = {:a => "a", :b => "b"}
136
+ @double.should_receive(:random_call).with(:a => "a", :b => "b")
137
+ @double.random_call(opts)
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end