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
data/lib/spy/double.rb ADDED
@@ -0,0 +1,11 @@
1
+ class Spy
2
+ class Double
3
+ def initialize(name, *args)
4
+ @name = name
5
+
6
+ if args.size > 0
7
+ Spy.on(self,*args)
8
+ end
9
+ end
10
+ end
11
+ end
data/lib/spy/dsl.rb ADDED
@@ -0,0 +1,7 @@
1
+ class Spy
2
+ module Dsl
3
+ def spy
4
+ Spy
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ class Spy
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,39 @@
1
+ require "rspec/autorun"
2
+ require "spy"
3
+ require "pry"
4
+ require "pry-nav"
5
+
6
+ RSpec::Matchers.define :include_method do |expected|
7
+ match do |actual|
8
+ actual.map { |m| m.to_s }.include?(expected.to_s)
9
+ end
10
+ end
11
+
12
+ RSpec.configure do |config|
13
+ config.color_enabled = true
14
+ config.order = :random
15
+ config.run_all_when_everything_filtered = true
16
+ config.treat_symbols_as_metadata_keys_with_true_values = true
17
+ config.filter_run_including :focus
18
+ config.filter_run_excluding :broken => true
19
+
20
+
21
+ config.expect_with :rspec do |expectations|
22
+ expectations.syntax = :expect
23
+ end
24
+
25
+ old_verbose = nil
26
+ config.before(:each, :silence_warnings) do
27
+ old_verbose = $VERBOSE
28
+ $VERBOSE = nil
29
+ end
30
+
31
+ config.after(:each, :silence_warnings) do
32
+ $VERBOSE = old_verbose
33
+ end
34
+
35
+ config.after(:each) do
36
+ Spy.teardown
37
+ end
38
+ end
39
+
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+
3
+ describe "and_call_through" do
4
+ context "on a partial mock object" do
5
+ let(:klass) do
6
+ Class.new do
7
+ def meth_1
8
+ :original
9
+ end
10
+
11
+ def meth_2(x)
12
+ yield x, :additional_yielded_arg
13
+ end
14
+
15
+ def self.new_instance
16
+ new
17
+ end
18
+ end
19
+ end
20
+
21
+ let(:instance) { klass.new }
22
+
23
+ it 'passes the received message through to the original method' do
24
+ spy = Spy.on(instance, :meth_1).and_call_through
25
+ expect(instance.meth_1).to eq(:original)
26
+ expect(spy).to have_been_called
27
+ end
28
+
29
+ it 'passes args and blocks through to the original method' do
30
+ spy = Spy.on(instance, :meth_2).and_call_through
31
+ value = instance.meth_2(:submitted_arg) { |a, b| [a, b] }
32
+ expect(value).to eq([:submitted_arg, :additional_yielded_arg])
33
+ expect(spy).to have_been_called
34
+ end
35
+
36
+ it 'works for singleton methods' do
37
+ def instance.foo; :bar; end
38
+ spy = Spy.on(instance, :foo).and_call_through
39
+ expect(instance.foo).to eq(:bar)
40
+ expect(spy).to have_been_called
41
+ end
42
+
43
+ it 'works for methods added through an extended module' do
44
+ instance.extend Module.new { def foo; :bar; end }
45
+ spy = Spy.on(instance, :foo).and_call_through
46
+ expect(instance.foo).to eq(:bar)
47
+ expect(spy).to have_been_called
48
+ end
49
+
50
+ it "works for method added through an extended module onto a class's ancestor" do
51
+ sub_sub_klass = Class.new(Class.new(klass))
52
+ klass.extend Module.new { def foo; :bar; end }
53
+ spy = Spy.on(sub_sub_klass, :foo).and_call_through
54
+ expect(sub_sub_klass.foo).to eq(:bar)
55
+ expect(spy).to have_been_called
56
+ end
57
+
58
+ it "finds the method on the most direct ancestor even if the method " +
59
+ "is available on more distant ancestors" do
60
+ klass.extend Module.new { def foo; :klass_bar; end }
61
+ sub_klass = Class.new(klass)
62
+ sub_klass.extend Module.new { def foo; :sub_klass_bar; end }
63
+ spy = Spy.on(sub_klass, :foo).and_call_through
64
+ expect(sub_klass.foo).to eq(:sub_klass_bar)
65
+ expect(spy).to have_been_called
66
+ end
67
+
68
+ it 'works for class methods defined on a superclass' do
69
+ subclass = Class.new(klass)
70
+ spy = Spy.on(subclass, :new_instance).and_call_through
71
+ expect(subclass.new_instance).to be_a(subclass)
72
+ expect(spy).to have_been_called
73
+ end
74
+
75
+ it 'works for class methods defined on a grandparent class' do
76
+ sub_subclass = Class.new(Class.new(klass))
77
+ spy = Spy.on(sub_subclass, :new_instance).and_call_through
78
+ expect(sub_subclass.new_instance).to be_a(sub_subclass)
79
+ expect(spy).to have_been_called
80
+ end
81
+
82
+ it 'works for class methods defined on the Class class' do
83
+ spy = Spy.on(klass, :new).and_call_through
84
+ expect(klass.new).to be_an_instance_of(klass)
85
+ expect(spy).to have_been_called
86
+ end
87
+
88
+ it "works for instance methods defined on the object's class's superclass" do
89
+ subclass = Class.new(klass)
90
+ inst = subclass.new
91
+ spy = Spy.on(inst, :meth_1).and_call_through
92
+ expect(inst.meth_1).to eq(:original)
93
+ expect(spy).to have_been_called
94
+ end
95
+
96
+ it 'works for aliased methods' do
97
+ klass = Class.new do
98
+ class << self
99
+ alias alternate_new new
100
+ end
101
+ end
102
+
103
+ spy = Spy.on(klass, :alternate_new).and_call_through
104
+ expect(klass.alternate_new).to be_an_instance_of(klass)
105
+ expect(spy).to have_been_called
106
+ end
107
+
108
+ context 'on an object that defines method_missing' do
109
+ before do
110
+ klass.class_eval do
111
+ def respond_to_missing?(name, _)
112
+ if name.to_s == "greet_jack"
113
+ true
114
+ else
115
+ super
116
+ end
117
+ end
118
+
119
+ def method_missing(name, *args)
120
+ if name.to_s == "greet_jack"
121
+ "Hello, jack"
122
+ else
123
+ super
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ it 'works when the method_missing definition handles the message' do
130
+ spy = Spy.on(instance, :greet_jack).and_call_through
131
+ expect(instance.greet_jack).to eq("Hello, jack")
132
+ expect(spy).to have_been_called
133
+ end
134
+
135
+ it 'raises an error on invocation if method_missing does not handle the message' do
136
+ Spy.new(instance, :not_a_handled_message).hook(force: true).and_call_through
137
+
138
+ # Note: it should raise a NoMethodError (and usually does), but
139
+ # due to a weird rspec-expectations issue (see #183) it sometimes
140
+ # raises a `NameError` when a `be_xxx` predicate matcher has been
141
+ # recently used. `NameError` is the superclass of `NoMethodError`
142
+ # so this example will pass regardless.
143
+ # If/when we solve the rspec-expectations issue, this can (and should)
144
+ # be changed to `NoMethodError`.
145
+ expect {
146
+ instance.not_a_handled_message
147
+ }.to raise_error(NameError, /not_a_handled_message/)
148
+ end
149
+ end
150
+ end
151
+ end
152
+
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+
3
+ describe RSpec::Mocks::Mock do
4
+
5
+ let(:obj) { double }
6
+
7
+ describe "#and_yield" do
8
+ context "with eval context as block argument" do
9
+
10
+ it "evaluates the supplied block as it is read" do
11
+ evaluated = false
12
+ Spy.on(obj, :method_that_accepts_a_block).and_yield do |eval_context|
13
+ evaluated = true
14
+ end
15
+ expect(evaluated).to be_true
16
+ end
17
+
18
+ it "passes an eval context object to the supplied block" do
19
+ Spy.on(obj, :method_that_accepts_a_block).and_yield do |eval_context|
20
+ expect(eval_context).not_to be_nil
21
+ end
22
+ end
23
+
24
+ it "evaluates the block passed to the stubbed method in the context of the supplied eval context" do
25
+ expected_eval_context = nil
26
+ actual_eval_context = nil
27
+
28
+ Spy.on(obj, :method_that_accepts_a_block).and_yield do |eval_context|
29
+ expected_eval_context = eval_context
30
+ end
31
+
32
+ obj.method_that_accepts_a_block do
33
+ actual_eval_context = self
34
+ end
35
+
36
+ expect(actual_eval_context).to equal(expected_eval_context)
37
+ end
38
+
39
+ context "and no yielded arguments" do
40
+
41
+ it "passes when expectations set on the eval context are met" do
42
+ configured_eval_context = nil
43
+ Spy.on(obj, :method_that_accepts_a_block).and_yield do |eval_context|
44
+ configured_eval_context = eval_context
45
+ configured_eval_context.should_receive(:foo)
46
+ end
47
+
48
+ obj.method_that_accepts_a_block do
49
+ foo
50
+ end
51
+
52
+ configured_eval_context.rspec_verify
53
+ end
54
+
55
+ it "fails when expectations set on the eval context are not met" do
56
+ configured_eval_context = nil
57
+ Spy.on(obj, :method_that_accepts_a_block).and_yield do |eval_context|
58
+ configured_eval_context = eval_context
59
+ configured_eval_context.should_receive(:foo)
60
+ end
61
+
62
+ obj.method_that_accepts_a_block do
63
+ # foo is not called here
64
+ end
65
+
66
+ expect { configured_eval_context.rspec_verify }.to raise_error(RSpec::Mocks::MockExpectationError)
67
+ end
68
+
69
+ end
70
+
71
+ context "and yielded arguments" do
72
+
73
+ it "passes when expectations set on the eval context and yielded arguments are met" do
74
+ configured_eval_context = nil
75
+ yielded_arg = Object.new
76
+ Spy.on(obj, :method_that_accepts_a_block).and_yield(yielded_arg) do |eval_context|
77
+ configured_eval_context = eval_context
78
+ configured_eval_context.should_receive(:foo)
79
+ yielded_arg.should_receive(:bar)
80
+ end
81
+
82
+ obj.method_that_accepts_a_block do |obj|
83
+ obj.bar
84
+ foo
85
+ end
86
+
87
+ configured_eval_context.rspec_verify
88
+ yielded_arg.rspec_verify
89
+ end
90
+
91
+ it "fails when expectations set on the eval context and yielded arguments are not met" do
92
+ configured_eval_context = nil
93
+ yielded_arg = Object.new
94
+ Spy.on(obj, :method_that_accepts_a_block).and_yield(yielded_arg) do |eval_context|
95
+ configured_eval_context = eval_context
96
+ configured_eval_context.should_receive(:foo)
97
+ yielded_arg.should_receive(:bar)
98
+ end
99
+
100
+ obj.method_that_accepts_a_block do |obj|
101
+ # obj.bar is not called here
102
+ # foo is not called here
103
+ end
104
+
105
+ expect { configured_eval_context.rspec_verify }.to raise_error(RSpec::Mocks::MockExpectationError)
106
+ expect { yielded_arg.rspec_verify }.to raise_error(RSpec::Mocks::MockExpectationError)
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+ end
113
+ end
114
+
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe "An RSpec Mock" do
4
+ it "hides internals in its inspect representation" do
5
+ m = double('cup')
6
+ expect(m.inspect).to match /#<RSpec::Mocks::Mock:0x[a-f0-9.]+ @name="cup">/
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Double" do
4
+ let(:test_double) { double }
5
+
6
+ specify "when one example has an expectation inside the block passed to should_receive" do
7
+ Spy.stub(test_double, :msg).and_return do |arg|
8
+ expect(arg).to be_true #this call exposes the problem
9
+ end
10
+ begin
11
+ test_double.msg(false)
12
+ rescue Exception
13
+ end
14
+ end
15
+
16
+ specify "then the next example should behave as expected instead of saying" do
17
+ test_double_spy = Spy.stub(test_double, :foobar)
18
+ test_double.foobar
19
+ expect(test_double_spy).to have_been_called
20
+ test_double.foobar
21
+ expect(test_double_spy.calls.count).to equal(2)
22
+ end
23
+ end
24
+
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ module BugReport496
4
+ describe "a message expectation on a base class object" do
5
+ class BaseClass
6
+ end
7
+
8
+ class SubClass < BaseClass
9
+ end
10
+
11
+ it "is received" do
12
+ new_spy = Spy.on(BaseClass, :new)
13
+ SubClass.new
14
+ expect(new_spy.calls.count).to equal(1)
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ module BugReport600
4
+ describe "stubbing a class method" do
5
+ class ExampleClass
6
+ def self.method_that_uses_define_method
7
+ define_method "defined_method" do |attributes|
8
+ load_address(address, attributes)
9
+ end
10
+ end
11
+ end
12
+
13
+ it "works" do
14
+ define_method_spy = Spy.on(ExampleClass, :define_method)
15
+ ExampleClass.method_that_uses_define_method
16
+
17
+ expect(define_method_spy).to have_been_called_with("defined_method")
18
+ end
19
+
20
+ it "restores the original method" do
21
+ ExampleClass.method_that_uses_define_method
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ module Bug7611
4
+ describe "A Partial Mock" do
5
+ class Foo; end
6
+ class Bar < Foo; end
7
+
8
+ it "respects subclasses" do
9
+ Spy.on(Foo, :new).and_return(Object.new)
10
+ end
11
+
12
+ it "should" do
13
+ expect(Bar.new.class).to eq Bar
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe "An object where respond_to? is true and does not have method" do
4
+ # When should_receive(message) is sent to any object, the Proxy sends
5
+ # respond_to?(message) to that object to see if the method should be proxied.
6
+ #
7
+ # If respond_to? itself is proxied, then when the Proxy sends respond_to?
8
+ # to the object, the proxy is invoked and responds yes (if so set in the spec).
9
+ # When the object does NOT actually respond to `message`, an exception is thrown
10
+ # when trying to proxy it.
11
+ #
12
+ # The fix was to keep track of whether `respond_to?` had been proxied and, if
13
+ # so, call the munged copy of `respond_to?` on the object.
14
+
15
+ it "does not raise an exception for Object" do
16
+ obj = Object.new
17
+ obj.should_receive(:respond_to?).with(:foobar).and_return(true)
18
+ obj.should_receive(:foobar).and_return(:baz)
19
+ expect(obj.respond_to?(:foobar)).to be_true
20
+ expect(obj.foobar).to eq :baz
21
+ end
22
+
23
+ it "does not raise an exception for mock" do
24
+ obj = double("obj")
25
+ obj.should_receive(:respond_to?).with(:foobar).and_return(true)
26
+ obj.should_receive(:foobar).and_return(:baz)
27
+ expect(obj.respond_to?(:foobar)).to be_true
28
+ expect(obj.foobar).to eq :baz
29
+ end
30
+
31
+ end