File: object_double_spec.rb

package info (click to toggle)
ruby-rspec 3.13.0c0e0m0s1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,856 kB
  • sloc: ruby: 70,868; sh: 1,423; makefile: 99
file content (136 lines) | stat: -rw-r--r-- 5,583 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
require 'support/doubled_classes'

module RSpec
  module Mocks
    RSpec.describe 'An object double' do
      let(:loaded_instance) { LoadedClass.new(1, 2) }

      it 'can replace an unloaded constant' do
        o = object_double("LoadedClass::NOINSTANCE").as_stubbed_const

        expect(LoadedClass::NOINSTANCE).to eq(o)

        expect(o).to receive(:undefined_instance_method)
        o.undefined_instance_method
      end

      it 'can replace a constant by name and verify instance methods' do
        o = object_double("LoadedClass::INSTANCE").as_stubbed_const

        expect(LoadedClass::INSTANCE).to eq(o)

        prevents { expect(o).to receive(:undefined_instance_method) }
        prevents { expect(o).to receive(:defined_class_method) }
        prevents { o.defined_instance_method }

        expect(o).to receive(:defined_instance_method)
        o.defined_instance_method
        expect(o).to receive(:defined_private_method)
        o.send :defined_private_method
      end

      it 'can create a double that matches the interface of any arbitrary object' do
        o = object_double(loaded_instance)

        prevents { expect(o).to receive(:undefined_instance_method) }
        prevents { expect(o).to receive(:defined_class_method) }
        prevents { o.defined_instance_method }

        expect(o).to receive(:defined_instance_method)
        o.defined_instance_method
        expect(o).to receive(:defined_private_method)
        o.send :defined_private_method
      end

      it 'does not allow transferring constants to an object' do
        expect {
          object_double("LoadedClass::INSTANCE").
            as_stubbed_const(:transfer_nested_constants => true)
        }.to raise_error(/Cannot transfer nested constants/)
      end

      it 'does not allow as_stubbed_constant for real objects' do
        expect {
          object_double(loaded_instance).as_stubbed_const
        }.to raise_error(/Can not perform constant replacement with an anonymous object/)
      end

      it 'is not a module' do
        expect(object_double("LoadedClass::INSTANCE")).to_not be_a(Module)
      end

      it 'validates `with` args against the method signature when stubbing a method' do
        dbl = object_double(loaded_instance)
        prevents(/Wrong number of arguments. Expected 2, got 3./) {
          allow(dbl).to receive(:instance_method_with_two_args).with(3, :foo, :args)
        }
      end

      context "when a loaded object constant has previously been stubbed with an object" do
        before { stub_const("LoadedClass::INSTANCE", Object.new) }

        it "uses the original object to verify against for `object_double('ConstName')`" do
          o = object_double("LoadedClass::INSTANCE")
          allow(o).to receive(:defined_instance_method)
          prevents { allow(o).to receive(:undefined_meth) }
        end

        it "uses the stubbed const value to verify against for `object_double(ConstName)`, " \
           "which probably isn't what the user wants, but there's nothing else we can do since " \
           "we can't get the constant name from the given object and thus cannot interrogate " \
           "our stubbed const registry to see it has been stubbed" do
          o = object_double(LoadedClass::INSTANCE)
          prevents { allow(o).to receive(:defined_instance_method) }
        end
      end

      context "when a loaded object constant has previously been stubbed with a class" do
        before { stub_const("LoadedClass::INSTANCE", Class.new) }

        it "uses the original object to verify against for `object_double('ConstName')`" do
          o = object_double("LoadedClass::INSTANCE")
          allow(o).to receive(:defined_instance_method)
          prevents { allow(o).to receive(:undefined_meth) }
        end

        it "uses the original object to verify against for `object_double(ConstName)`" do
          o = object_double(LoadedClass::INSTANCE)
          allow(o).to receive(:defined_instance_method)
          prevents { allow(o).to receive(:undefined_meth) }
        end
      end

      context "when an unloaded object constant has previously been stubbed with an object" do
        before { stub_const("LoadedClass::NOINSTANCE", LoadedClass::INSTANCE) }

        it "treats it as being unloaded for `object_double('ConstName')`" do
          o = object_double("LoadedClass::NOINSTANCE")
          allow(o).to receive(:undefined_method)
        end

        it "uses the stubbed const value to verify against for `object_double(ConstName)`, " \
           "which probably isn't what the user wants, but there's nothing else we can do since " \
           "we can't get the constant name from the given object and thus cannot interrogate " \
           "our stubbed const registry to see it has been stubbed" do
          o = object_double(LoadedClass::NOINSTANCE)
          allow(o).to receive(:defined_instance_method)
          prevents { allow(o).to receive(:undefined_method) }
        end
      end

      context "when an unloaded object constant has previously been stubbed with a class" do
        before { stub_const("LoadedClass::NOINSTANCE", Class.new) }

        it "treats it as being unloaded for `object_double('ConstName')`" do
          o = object_double("LoadedClass::NOINSTANCE")
          allow(o).to receive(:undefined_method)
        end

        it "treats it as being unloaded for `object_double(ConstName)`" do
          o = object_double(LoadedClass::NOINSTANCE)
          allow(o).to receive(:undefined_method)
        end
      end
    end
  end
end